В awk или nawk, как мне использовать последнее вхождение символа вертикальной черты в качестве разделителя полей, что дает мне 2 поля?

Я бы предпочел не использовать функции, предназначенные только для gawk, поскольку мне нужно будет запускать это на различных разновидностях UNIX, и не все из них имеют gawk. У меня есть файл с такими строками:

^myfile\..*\.(pork|beef)$|send -d j
^myfile\..*\.(chicken|turkey|quail)$|send -d q
^myfile\..*\.cheese$|send -d u

Иногда, но не всегда, первое поле содержит один или несколько символов вертикальной черты. Символы после последней вертикальной черты можно с уверенностью назвать полем 2.


person Lambert    schedule 28.07.2015    source источник


Ответы (3)


Я не уверен, что это полностью переносимо, но я думаю, что это:

awk '{
    # Find the position of the last "|" in the line.
    p=match($0, /\|[^|]*$/)

    # "Split" the line into two fields around that position.
    a[1]=substr($0, 1, p-1)
    a[2]=substr($0, p+1)

    printf "[%s] [%s]\n", a[1], a[2]
}' file.in

Как указано Эдом Мортоном в комментариях, использование p здесь необязательно, поскольку функция awk match также устанавливает переменную RSTART в позицию в строке, где совпало регулярное выражение, поэтому приведенное выше также можно было бы записать таким образом:

awk '{
    # Find the last "|" in the line.
    match($0, /\|[^|]*$/)

    # "Split" the line into two fields around that position (using the RSTART variable from the match() call).
    a[1]=substr($0, 1, RSTART-1)
    a[2]=substr($0, RSTART+1)

    printf "[%s] [%s]\n", a[1], a[2]
}' file.in'

На самом деле эффективное выполнение этой точной задачи является примером match() в ох Гримуар.

person Etan Reisner    schedule 28.07.2015
comment
Слэм-данк! Да, это портативно. Отлично работает в Linux, HP-UX, AIX и Solaris с /usr/xpg4/bin/awk или nawk. Спасибо! - person Lambert; 28.07.2015
comment
@EdMorton Сначала я собирался использовать RSTART, но подумал, что p= немного более самодокументируемый. - person Etan Reisner; 28.07.2015
comment
@EdMorton Хороший улов. Спасибо. - person Etan Reisner; 28.07.2015

Вы можете просто установить FS в $|:

$ awk -F'[$][|]' '{printf "[%s$] [%s]\n", $1, $2}' file
[^myfile\..*\.(pork|beef)$] [send -d j]
[^myfile\..*\.(chicken|turkey|quail)$] [send -d q]
[^myfile\..*\.cheese$] [send -d u]

Вы можете прибить $ обратно к концу $1, если хотите:

$ awk -F'[$][|]' '{$1=$1"$"; printf "[%s] [%s]\n", $1, $2}' file
[^myfile\..*\.(pork|beef)$] [send -d j]
[^myfile\..*\.(chicken|turkey|quail)$] [send -d q]
[^myfile\..*\.cheese$] [send -d u]

Другой подход, если вы предпочитаете, будет:

$ awk '{f1=f2=$0; sub(/\|[^|]*$/,"",f1); sub(/.*\|/,"",f2); printf "[%s] [%s]\n", f1, f2}' file
[^myfile\..*\.(pork|beef)$] [send -d j]
[^myfile\..*\.(chicken|turkey|quail)$] [send -d q]
[^myfile\..*\.cheese$] [send -d u]
person Ed Morton    schedule 28.07.2015
comment
Да, но как вы можете быть уверены, что перед последней трубой всегда стоит $? - person Casimir et Hippolyte; 28.07.2015
comment
Поскольку OP показал в своем образце ввода, что всегда есть $ перед последней трубой, и вы можете видеть, что в начале каждого первого поля есть привязка начала строки, и поэтому разумно ожидать привязку к концу строки в конце каждого, как показано в образце ввода. Нет смысла усложнять ответ без необходимости, и ОП всегда может предоставить другой ввод, если этот ответ не всегда будет работать. - person Ed Morton; 28.07.2015
comment
Да, это очень интересно и есть над чем подумать. Якоря ^ и $ в поле 1 не являются обязательными. Это типичные характеристики данных в поле 1, и я полагаю, мы могли бы превратить их в жесткие требования. - person Lambert; 29.07.2015

Вы можете сделать это и так (например, я выбираю вкладку в качестве нового разделителя):

awk -vRS='[|]' -vORS='' 'NR>1{printf /\n/?"\t":"|"}1' file
person Casimir et Hippolyte    schedule 28.07.2015
comment
Отсутствие пробела между -v и именем переменной делает ее специфичной для gawk, а многосимвольный RS не определен POSIX, поэтому, хотя gawk будет считать, что вы имеете в виду RE, другие awks могут просто использовать первый символ или делать с ним что-то еще. . Чтобы сделать приведенный выше POSIX, это будет -v RS='|' - вам не нужно экранировать |, поскольку это всего лишь один символ, и поэтому он имеет буквальное значение во всех awks. - person Ed Morton; 29.07.2015