AWK: есть ли способ установить OFS как FS, если это регулярное выражение?

в awk разделитель поля (или записи)FS (или RS) можно задать как регулярное выражение. Он отлично работает для получения любого отдельного поля, но как только вы установите эти поля, разделители полей "ушли".

echo "a|b-c|d" | awk 'BEGIN{FS="[|-]"} {="z"}1'
a b z d 

в этом случае разделитель выходного поля OFS по умолчанию установлено как пробел.

к сожалению, такого рода заявление OFS=FS="[|-]" не работает, потому что он устанавливает OFS как litteral строку.

Я понимаю что для awk может быть сложно выбрать разделитель выходного поля, если есть несколько вариантов, но в случае отсутствия новых полей текущие могут остаться.

Итак, есть ли простой способ, чтобы установить OFS быть точно таким же регулярным выражением, как FS, Так что я получаю это?

echo "a|b-c|d" | awk '... {="z"}1'
a|b-z|d

кроме того, есть ли способ захватить все разделители, например, в массиве?

тот же вопрос также относится к разделителю записей RS (и связанные с ним ORS)

3 ответов


как вы уже упоминали, нет никакого способа установить OFS динамически на основании FS это использовалось в каждом случае. Если регулярное выражение было в RS вместо FS, вы могли бы использовать RT (на самом деле, я просто вижу, что ответ анубхавы делает это, хорошо!).

однако, есть другой способ, если у вас есть GNU awk: как видно из замена столбца awk с сохранением формата (ответ Эда Мортона), вы можете использовать split() и, особенно, его 4-й аргумент. Почему? Потому что он хранит разделитель между каждым кусочком:

gawk 'BEGIN{FS="[|-]"}                     # set FS
     {split(, a, FS, seps)               # split based on FS and ...
                                           # ...  store pieces in the array seps()
      a[3]="z"                             # change the 3rd field
      for (i=1;i<=NF;i++)                  # print the data back
           printf "%s%s", a[i], seps[i]    # keeping the separators
      print ""                             # print a new line
     }'

как один-лайнер:

$ gawk 'BEGIN{FS="[|-]"} {split(, a, FS, seps); a[3]="z"; for (i=1;i<=NF;i++) printf "%s%s", a[i], seps[i]; print ""}' <<< "a|b-c|d"
a|b-z|d

split (string, array [, fieldsep [, seps ] ])

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


awk переписывает каждую запись с помощью OFS Если вы измените любое значение файла, используя $N=<whatever> (где N-номер поля).

так как вы используете несколько разделителей в FS вы не можете использовать OFS=FS.

если у вас gnu awk затем вы можете использовать RS и RT решение:

s="a|b-c|d"
awk -v RS='[-|]' 'NR==3{="z"} {printf "%s%s", , RT}' <<< "$s"

a|b-z|d

в качестве альтернативы вы можете использовать sed:

s="a|b-c|d"
sed -E 's/^(([^|-]+[|-]){2})[^|-]+/z/' <<< "$s"

a|b-z|d

поскольку вам явно не нужно работать с полями, просто обработайте $0 другими способами, как показано ниже с sub:

$ echo "a|b-c|d" | awk 'BEGIN{FS="[|-]"} {sub(/c/,"z")}1'
a|b-z|d