Как разбить строку с разделителями в массив в awk?


169

Как разбить строку , если она содержит символы трубы |в нем. Я хочу разделить их, чтобы быть в массиве.

Я попытался

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

Который работает отлично. Если моя строка похожа, "12|23|11"то как мне разбить их на массив?


3
Обратите внимание, что ваш вывод объединяет элементы массива без разделителя. Если вы хотите, чтобы они разделялись OFS, вставьте между ними запятые, чтобы они printрассматривались как отдельные аргументы.
— dubiousjim

Или вы можете использовать sed: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
«не работает для меня». особенно с двоеточиями между отображаемыми значениями и разбиением, настроенными для разделения на '|' ??? Опечатка? Всем удачи.
— Shellter

1
Лучше с некоторым синтаксическим объяснением.
— Олстон

2
Это не будет работать в GNU awk, потому что третий аргумент to split- это регулярное выражение и |специальный символ, который необходимо экранировать. Использованиеsplit($0, a, "\|")
— WhiteWind

1
@WhiteWind: еще один способ «убедиться» в том, что |он рассматривается как символ, а не как специальный символ, - поместить его между []: т.е. split($0, a, "[|]") # мне нравится это лучше, чем '\ |', в некоторых случаях, особенно как вариант regexp ( Perl против grep против .. других?) может иметь "|" в буквальном смысле и "\ |" рассматривается как разделитель регулярных выражений, а не наоборот ... 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
:::

Давайте процитируем man-страницу GNU awk :

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

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


просто упомяните, что вы используете gnu awk, а не обычный awk (который не хранит разделители в seps [] и имеет другие ограничения)
— Olivier Dulac

17

Пожалуйста, будьте более конкретны! Что вы подразумеваете под "это не работает"? Отправьте точный вывод (или сообщение об ошибке), вашу ОС и версию awk:

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

Или, используя split:

% 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

я = 0, потому что я использую ++ я после (не я ++).
— Димитр Радулов

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.