Як роздягнути кілька просторів до одного за допомогою sed?


69

sedна AIX - це не те, що я думаю, що повинно. Я намагаюся замінити декілька пробілів одним простором у висновку IOSTAT:

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

#iostat|grep "hdisk1"|sed -e"s/[ ]*/ /g"
 h d i s k 1 0 . 1 6 . 3 0 . 5 6 3 3 4 5 8 8 0 1 1 2 4 3 2 3 5 4

sed повинен шукати та замінювати (-ла) декілька пробілів (/ [] * /) з одним пробілом (/ /) для всієї групи (/ g) ... але це не тільки робиться ... пробіл кожного символу.

Що я роблю неправильно? Я знаю, що це має бути щось просте ... AIX 5300-06

редагувати: У мене є інший комп'ютер, який має 10+ жорстких дисків. Я використовую це як параметр для іншої програми для моніторингу.

Проблема, з якою я зіткнувся, полягала в тому, що "awk" {print $ 5} "не працював, тому що я використовую $ 1 тощо на вторинній стадії і помилявся з командою Print. Я шукав grep / sed / cut версію Що, здається, працює:

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

[] S були "0 або більше", коли я подумав, що вони означають "лише один". Зняття кронштейнів спрацювало. Три дуже гарні відповіді дійсно швидко ускладнюють вибір "відповіді".

Відповіді:


52

Використання grepзайвого, sedможе зробити те ж саме. Проблема полягає у використанні *цієї збіги також 0 пробілів, ви повинні використовувати \+замість цього:

iostat | sed -n '/hdisk1/s/ \+/ /gp'

Якщо ваш sedне підтримує \+метахар, то зробіть

iostat | sed -n '/hdisk1/s/  */ /gp'

Схоже, AIX не підтримує +, але видалення [], здається, зробило трюк.
WernerCD

Я спробував використовувати версію sed -n ... що трапляється, я маю інший комп'ютер, який має 10+ накопичувачів, тому він починає робити 1, 10, 11 і т.д. ... Я намагався додати пробіл / hdisk1 /, і це дало мені "не розпізнана функція". що, здається, працює >> іостат | grep "hdisk1" | sed -e's / * / / g '
WernerCD

67

/[ ]*/відповідає нулю або більше пробілів, тому порожній рядок між символами збігається.

Якщо ви намагаєтеся зіставити "один або кілька пробілів", використовуйте один із них:

... | sed 's/  */ /g'
... | sed 's/ \{1,\}/ /g'
... | tr -s ' '

Ааа ... [] робить це "необов’язковим". Це пояснює це.
WernerCD

5
@WernerCD, жоден не *робить його "необов'язковим". [ ]просто складає список символів, у яких лише один символ (пробіл). Саме кількісний показник *означає "нуль або більше попереднього"
glenn jackman

Ааа ... щоб бути точнішим, змінивши його з єдиного простору / * /, на подвійний простір - це те, що зробив тоді. Я готча.
WernerCD

Я намагався шукати шаблон, який шукає лише подвійні пробіли, і це спрацювало круто
minhas23

6
+1 для найпростішого tr -s ' 'рішення
Андрейс

12

Змініть свого *оператора на a +. Ви збігаєте нуль або більше попереднього символу, який відповідає кожному символу, тому що все, що не є пробілом, є ... гм ... нульові екземпляри простору. Потрібно відповідати ОДНОМ або більше. Насправді було б краще зіставити два і більше

Класичний символ символів також не потрібний для відповідності одному символу. Ви можете просто використовувати:

s/  \+/ /g

... якщо ви також не хочете співставляти вкладки чи інші види пробілів, тоді клас персонажів є гарною ідеєю.


Схоже, AIX не підтримує +.
WernerCD

1
@WernerCD: Потім спробуйте s/ */ /g(це з трьох пробілів, форматування коментарів їх згортає ). Оператор зірки зробить попередній символ необов’язковим, тому, якщо ви зіставляєте два чи більше з ним, вам потрібно самостійно зіставити перші два (два пробіли), потім додати третій пробіл та зірку, щоб зробити третій та наступні пробіли необов’язковими.
Калеб

3
@userunknown: Насправді я взагалі не змішую двох речей, всі інші - :) Заміняти один простір єдиним пробілом безглуздо, потрібно виконати цю дію лише на матчах, які мають принаймні два послідовних пробіли. Два заготовки та плюс-три заготовки та зірка - це саме те, що потрібно.
Калеб

@userunknown: Це не така вже й велика справа, це лише витрата небагато часу на обробку, і це скидає речі, такі як лічильники матчів.
Калеб

8

Ви завжди можете зіставити останнє явище в послідовності з будь-якого типу:

s/\(sequence\)*/\1/

І значить, ви на правильному шляху, але замість того, щоб замінювати послідовність пробілом - замініть його останнім його виникненням - єдиним пробілом. Таким чином , якщо послідовність просторів буде відповідати , то послідовність не зводиться до одного простору, але якщо нульова рядок збігається , то порожній рядок замінюється сама по собі - і ніякої шкоди, немає фолу. Так, наприклад:

sed 's/\( \)*/\1/g' <<\IN                                    
# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

IN

ВИХІД

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty: tin tout avg-cpu: % user % sys % idle % iowait
 0.2 31.8 9.7 4.9 82.9 2.5

Disks: % tm_act Kbps tps Kb_read Kb_wrtn
hdisk9 0.2 54.2 1.1 1073456960 436765896
hdisk7 0.2 54.1 1.1 1070600212 435678280
hdisk8 0.0 0.0 0.0 0 0
hdisk6 0.0 0.0 0.0 0 0
hdisk1 0.1 6.3 0.5 63344916 112429672
hdisk0 0.1 5.0 0.2 40967838 98574444
cd0 0.0 0.0 0.0 0 0
hdiskpower1 0.2 108.3 2.3 2144057172 872444176

# iostat | grep hdisk1
hdisk1 0.1 6.3 0.5 63345700 112431123

Все, що сказано, напевно, набагато краще, щоб у цій ситуації повністю уникати регулярних виразів і робити замість цього:

tr -s \  <infile

4
+1 для простоти реальної відповіді,iostat | tr -s \
Wildcard

'tr -s \' - це те саме, що 'tr -s \ ". Змусив мене зрозуміти, що простір можна передавати як аргумент у рядку, втечавши з "\". Я бачу, що він може бути використаний і в скриптах оболонки. Класне застосування.
випадкова речовинаOfLivingThing

5

Зауважте, що ви також можете робити те, що намагаєтесь, тобто

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

по

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$re"; done

що може бути особливо корисно, якщо пізніше ви також спробуєте отримати доступ до інших полів та / або обчислити щось подібне:

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$(( re/1024 )) Mb"; done

Дуже хороша. Перша версія працює. Мої ящики AIX, схоже, не схожі на другу. Усі три поля виводяться: "$ [re / 1024] Mb". Інструмент моніторингу, який я використовую, має конверсії для звітів, тому для мене це не "потрібна" річ, але мені це подобається.
WernerCD

@enzotib Дякую за виправлення while.
rozcietrzewiacz

@WernerCD Ах, це $[ .. ], мабуть, доступно в останніх версіях bash (можливо, і zsh теж). $(( .. ))Натомість я оновив відповідь на більш портативний .
rozcietrzewiacz

Це зробило трюк. Мені доведеться це подивитися. Запаморочливий.
WernerCD

0

Ви можете використовувати наступний сценарій для перетворення декількох пробілів в один простір, TAB або будь-який інший рядок:

$ ls | compress_spaces.sh       # converts multiple spaces to one
$ ls | compress_spaces.sh TAB   # converts multiple spaces to a single tab character
$ ls | compress_spaces.sh TEST  # converts multiple spaces to the phrase TEST
$ compress_spaces.sh help       # show the help for this command

compress_spaces.sh

function show_help()
{
  IT=$(CAT <<EOF

  usage: {REPLACE_WITH}

  NOTE: If you pass in TAB, then multiple spaces are replaced with a TAB character

  no args -> multiple spaces replaced with a single space
  TAB     -> multiple spaces replaced with a single tab character
  TEST    -> multiple spaces replaced with the phrase "TEST"

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show help if we're not getting data from stdin
if [ -t 0 ]; then
  show_help
fi

REPLACE_WITH=${1:-' '}

if [ "$REPLACE_WITH" == "tab" ]
then
  REPLACE_WITH=$'\t'
fi
if [ "$REPLACE_WITH" == "TAB" ]
then
  REPLACE_WITH=$'\t'
fi

sed "s/ \{1,\}/$REPLACE_WITH/gp"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.