Як розділити розділений рядок на масив в 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.