Используя awk, как мне перепечатать найденный шаблон с новым символом строки?

У меня есть текстовый файл в формате:

aaa: bcd;bcd;bcddd;aaa:bcd;bcd;bcd; 

Где "BCD" может быть любой длиной любых символов, кроме ; или :

Я хочу распечатать текстовый файл в формате:

aaa: bcd;bcd;bcddd;
aaa: bcd;bcd;bcd;

-и т.д-

Мой подход к этой проблеме заключался в том, чтобы изолировать шаблон «;...:», а затем перепечатать этот шаблон без начального ;.

Я пришел к выводу, что для этого мне придется использовать «gsub» awk, но я понятия не имею, как воспроизвести шаблон или как снова напечатать шаблон с добавленным новым символом строки 1 в мой шаблон.

Это возможно? Если нет, то не могли бы вы указать мне способ решения этой проблемы?


person Max    schedule 29.03.2012    source источник
comment
Вы просто пытаетесь вставить новую строку после каждого третьего «;»? Или есть нечто большее, чем это.   -  person William Pursell    schedule 29.03.2012
comment
Количество ; не является последовательным, поэтому, к сожалению, не может быть таким простым, как после каждого третьего.   -  person Max    schedule 29.03.2012


Ответы (4)


Мы не можем быть уверены в изменчивости частей aaa или bcd; предположительно, каждый из них может быть почти чем угодно.

Вероятно, вам следует искать:

  • последовательность из одного или нескольких символов без двоеточия и точки с запятой, за которыми следует двоеточие,
  • with one or more repeats of:
    • a series of one or more non-colon, non-semicolon characters followed by a semi-colon

Это составляет единицу, которую вы хотите сопоставить.

/[^:;]+:([^:;]+;)+/

При этом вы можете заменить то, что было найдено, на то же самое, за которым следует новая строка, а затем распечатать результат. Единственная хитрость — избегать лишних строк новой строки.

Пример скрипта:

{
echo "aaa: bcd;bcd;bcddd;aaa:bcd;bcd;bcd;" 
echo "aaz: xcd;ycd;bczdd;baa:bed;bid;bud;"
} |
awk '{ gsub(/[^:;]+:([^:;]+;)+/, "&\n"); sub(/\n+$/, ""); print }'

Пример вывода

aaa: bcd;bcd;bcddd;
aaa:bcd;bcd;bcd;
aaz: xcd;ycd;bczdd;
baa:bed;bid;bud;

Перефразируя вопрос в комментарии:

Почему регулярное выражение не включает символы перед двоеточием (именно для этого оно и предназначено, но я не понимаю почему)? Я не понимаю, что «ломает» или завершает регулярное выражение.

Как я пытался объяснить выше, вы ищете то, что мы можем назвать «словами», то есть последовательности символов, которые не являются ни двоеточием, ни точкой с запятой. В регулярном выражении это [^:;]+, что означает один или несколько (+) класса отрицательных символов, один или несколько символов без двоеточия и точки с запятой.

Предположим, что пробелы в регулярном выражении не имеют значения. Мы можем разделить регулярное выражение следующим образом:

    / [^:;]+ : ( [^:;]+ ; ) + /

Косые черты просто отмечают концы, конечно. Первый кластер — это слово; тогда есть двоеточие. Затем идет группа, заключенная в круглые скобки, помеченная + в конце. Это означает, что содержимое группы должно встречаться хотя бы один раз и может встречаться сколько угодно раз. Что внутри группы? Ну, слово, за которым следует точка с запятой. Это не обязательно должно быть одно и то же слово каждый раз, но там должно быть слово. Если что-то может произойти ноль или более раз, то вы, конечно, используете * вместо +.

Ключом к остановке регулярных выражений является то, что aaa: в середине первой строки не состоит из слова, за которым следует точка с запятой; это слово, за которым следует двоеточие. Таким образом, регулярное выражение должно быть остановлено до этого, потому что aaa: не соответствует группе. Таким образом, gsub() находит первую последовательность и заменяет этот текст тем же материалом и новой строкой (конечно, это "&\n"). Затем он (gsub()) возобновляет поиск сразу после окончания замещающего материала, и, о чудо, есть слово, за которым следует двоеточие, и несколько слов, за которыми следует точка с запятой, так что есть второе совпадение, которое нужно заменить исходным материалом, плюс новая линия.

Я думаю, что $0 должен содержать новую строку в конце строки. Поэтому без sub() для удаления завершающей новой строки print (косвенно из $0 с новой строкой) генерировало пустую строку, которую я не хотел выводить, поэтому я удалил лишние символы новой строки. Новая строка в конце $0 не будет соответствовать gsub(), поскольку за ней не следует двоеточие или точка с запятой.

person Jonathan Leffler    schedule 29.03.2012
comment
Благодарю вас! Это работает отлично, за исключением того, что по какой-то причине в моем текстовом файле есть один дополнительный пробел, но если я создаю текстовый файл, используя ввод, который вы использовали, его нет. Я просто выложу это, это не имеет большого значения. Я понимаю в основном все, кроме того, почему регулярное выражение не включает символы перед двоеточием. (Что является его предполагаемой целью, да, но я не понимаю, почему). Я понимаю, что вы сгруппировали точку с запятой с [^;:], но не понимаю, что ломает или заканчивает регулярное выражение. Что я пытаюсь понять, так это логику вашего gsub - person Max; 29.03.2012

Это может сработать для вас:

 awk '{gsub(/[^;:]*:/,"\n&");sub(/^\n/,"");gsub(/: */,": ")}1' file
  1. Добавляйте новую строку (\n) к любой строке, не содержащей ; или :, за которыми следует :
  2. Удалите любую новую строку, добавленную к началу строки.
  3. Замените любые :, за которыми не следуют ни одного пробела или много пробелов, на :, за которым следует один пробел.
  4. Распечатать все строки.

Или это:

 sed 's/;\([^;:]*: *\)/;\n\1 /g' file
person potong    schedule 29.03.2012

Не уверен, как это сделать в awk, но с sed это делает то, что, я думаю, вы хотите:

$ nl='
'
$ sed "s/\([^;]*:\)/\\${nl}\1/g" input

Первая команда устанавливает переменную оболочки $nl в строку, содержащую одну новую строку. Некоторые версии sed позволяют использовать \n внутри строки замены, но не все это позволяют. Это сохраняет любые пробелы, которые появляются после final ; и помещает его в начало строки. Чтобы избавиться от этого, вы можете сделать

$ sed "s/\([^;]*:\)/\\${nl}\1/g; s/\n */\\$nl/g" input
person William Pursell    schedule 29.03.2012

Обычные awk gsub() и sub() не позволяют указывать компоненты в заменяющих строках. Gnu awk — «gawk» — предоставляет «gensub()», что позволило бы «gensub(/(;) (.+:)/ ,"\1\n\2","г")"

person wharfie    schedule 29.03.2012