Як розділити розділений рядок на масив в awk?


169

Як розділити рядок, коли в ній є символи труби |. Я хочу розділити їх на масив.

я намагався

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Що чудово працює. Якщо мій рядок схожий на "12|23|11"то, як я розділити їх на масив?


3
Зауважте, що ваш вихід об'єднує елементи масиву без роздільника. Якщо ви натомість хотіли, щоб їх розділили OFS, вставте коми між ними, змушуючи printбачити їх окремими аргументами.
сумнівний

Або ви можете використовувати СЕД:echo "12:23:11" | sed "s/.*://"
мокрий

@slushy: ваша команда зовсім не те, що потрібно запитувачу. ваша команда ( echo "12:23:11" | sed "s/.*://") видаляє все до (включаючи) останнє ":", зберігаючи лише "11" ... воно працює, щоб отримати останнє число, але його потрібно буде змінити (важко читати), щоб отримати 2-е число і т. д. awk (і розкол awk) набагато більш елегантний і читабельний.
Олів'є

якщо вам потрібно розділити на одного символу, який ви можете використовуватиcut
ccpizza

Відповіді:


274

Ти намагався:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'

2
@Mohamed Saligh, якщо ви перебуваєте на Solaris, вам потрібно використовувати / usr / xpg4 / bin / awk , враховуючи довжину рядка.
Димитрій Радулов

5
"не працює для мене". особливо з двокрапками між відлунюваними значеннями та розділеними налаштуваннями, щоб розділити на '|' ??? Друкарська помилка? Успіхів усім.
обстріл

1
Краще з деяким синтаксичним поясненням.
Альстон

2
У GNU awk це не спрацює, оскільки третій аргумент - splitце регулярне вираження, і |це спеціальний символ, який потрібно уникнути. Використанняsplit($0, a, "\|")
WhiteWind

1
@WhiteWind: ще один спосіб "забезпечити" те, що |сприймається як char, а не особливий символ, - це поставити його між собою []: тобто, split($0, a, "[|]") # мені це подобається краще, ніж "\ |", в деяких випадках, особливо як якийсь варіант regexp ( perl vs grep vs .. інші?) можуть мати "|" інтепретується буквально і "\ |" розглядається як розділювач регулярних виразів, а не протилежний ... ymmv
Олів'є

119

Щоб розділити рядок на масив, awkми використовуємо функцію split():

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

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

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Ми можемо дати роздільник, наприклад ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Що еквівалентно встановленню через FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

У gawk ви також можете надати роздільник як регулярний вираз:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

І навіть подивіться, який роздільник був на кожному кроці, використовуючи його четвертий параметр:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Процитуємо сторінку чоловіка GNU awk :

split (рядок, масив [, fieldsep [, seps]])

Розділіть рядок на частини, розділені на поле fieldsep, і збережіть фрагменти в масиві, а розділові рядки в масиві seps . Перший шматок зберігається array[1], другий - array[2]тощо. Значення рядка третього аргументу, fieldsep , являє собою регулярний вираз, який описує, де розділити рядок (наскільки FS може бути регулярним виразом, що описує, де розділити записи вводу). Якщо поле поле пропущено, використовується значення FS . split()повертає кількість створених елементів. seps - це gawkрозширення, між seps[i]яким знаходиться рядок розділенняarray[i]і array[i+1]. Якщо fieldsep - це єдиний простір, то будь-який провідний пробіл переходить у seps[0]будь-який пробільний пробіл seps[n], де n - повернене значення split()(тобто кількість елементів у масиві).


просто згадайте, що ви використовуєте gnu awk, а не звичайний awk (який не зберігає роздільники в сепсах [], та має інші обмеження)
Олів'є Дулак

17

Будь ласка, будьте більш конкретними! Що ви маєте на увазі під "не працює"? Опублікуйте точний вихід (або повідомлення про помилку), вашу ОС та awk версію:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

Або, використовуючи спліт:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Редагувати: для Solaris вам потрібно буде використовувати POSIX awk ( / usr / xpg4 / bin / awk ), щоб правильно обробити 4000 полів.


for(i = 0або for(i = 1?
PiotrNycz

i = 0, тому що я використовую ++ i after (не i ++).
Димитрій Радулов

3
Гаразд - я цього не помічав. Я впевнений, що читабельніше було б for (i = 1; i <= n; ++i)...
PiotrNycz

5

Мені не подобається echo "..." | awk ...рішення, оскільки воно називає непотрібні forkта execсистемні дзвінки.

Я віддаю перевагу розчину Димитра з невеликим поворотом

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

Або трохи коротша версія:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

У цьому випадку вихідний запис складається разом, що є справжньою умовою, тому він надрукується.

У цьому конкретному випадку stdinперенаправлення може бути пошкоджено встановленням внутрішня змінна:

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

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

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

Результат у всіх випадках такий

112312

Я думаю, що кінцевим результатом мали стати посилання змінної масиву awk, незалежно від наведеного прикладу виводу друку. Але ви пропустили дійсно простий баш-футляр, щоб забезпечити свій кінцевий результат. T = '12: 23: 11 '; echo $ {T //:}
Даніель Лістон

@DanielListon Ви праві! Дякую! Я не знав, що слід / може бути залишений у цьому bashвиразі ...
TrueY

4

На насправді awkмає функцію під назвою «Поле введення Сепаратор Variable» посилання . Ось як це використовувати. Це насправді не масив, але він використовує внутрішні змінні $. Розщеплювати просту рядок простіше.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'



1

Жарт? :)

Як щодо echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Це мій вихід:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

тож я гадаю, що все-таки працює.


це через довжину струни? оскільки, довжина моєї струни - 4000. будь-які ідеї
Мохамед Саліг

1

Я знаю, що це якесь старе запитання, але я подумав, що, можливо, комусь подобається моя хитрість. Тим більше, що це рішення не обмежується конкретною кількістю предметів.

# Convert to an array
_ITEMS=($(echo "12|23|11" | tr '|' '\n'))

# Output array items
for _ITEM in "${_ITEMS[@]}"; do
  echo "Item: ${_ITEM}"
done

Вихід буде:

Item: 12
Item: 23
Item: 11
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.