Замена sed, отбрасывающая фрагменты текста

Я хотел использовать SED для поиска и замены небольшой строки текста в нескольких файлах.

В частности, я хочу выполнить следующую замену:

sed -e '/35=R/s/|131=.*|/|131=$UNIQUE|/g' $f

Это выполняется в сценарии bash, где $ f - имя файла.

Sed ищет строки, содержащие строку 35 = R, а затем имеет очень простое выражение для замены |131=.*| (что-либо после |131=) на |131=$UNIQUE|.

Кажется, что это отлично работает с некоторыми файлами, но в других случаях:

Например, рабочий пример:

До:

8=FIX.4.2|9=151|35=R|56=ABC|142=7848|50=STUFF|49=OTHERSTUFF|52=20250905-06:00:10.910|34=107|146=1|55=DE123|22=4|48=DE123|38=1|54=1|207=F|131=12ABC|10=243

После:

8=FIX.4.2|9=151|35=R|56=COBA|142=7848|50=STUFF|49=OTHERSTUFF|52=20250905-06:00:10.910|34=107|146=1|55=DE123|22=4|48=DE123|38=1|54=1|207=F|131=$UNIQUE|10=243

Однако в других случаях кажется, что на выходе отсутствуют большие блоки текста.

Пример не работает:

До:

8=FIX.4.2|9=147|35=R|34=15301|49=STUFF|52=20190905-15:27:54.305|56=OTHERSTUFF|115=STUFFY|131=1234abc|146=1|55=AB123|15=ZYX|22=4|38=1|48=AB123|54=2|207=STUFF|10=253

После:

8=FIX.4.2|9=147|35=R|34=15301|49=STUFF|52=20190905-15:27:54.305|56=OTHERSTUFF|115=STUFFY|131=$UNIQUE|10=253

Как видите, отсутствует все, что следует за трубкой после 131 = $ UNIQUE. Я новичок в выражениях и sed, поэтому, возможно, я неправильно понимаю замену. Любые указатели будут очень признательны.

Спасибо.


person Phill    schedule 18.09.2019    source источник


Ответы (3)


Замените .* на [^|]*, чтобы остановить .* перед первым |.

person Cyrus    schedule 18.09.2019

Выражение .* - «жадный». Это означает, что он попытается поймать как можно больше символов. В примерах он идет к крайнему правому символу |. Вы должны использовать это выражение:

sed -e '/35=R/s/|131=[^|]*|/|131=$UNIQUE|/g' $f
person oneastok    schedule 18.09.2019
comment
Я не верю, что поддержка ERE в Gnu sed включает в себя нежадное повторение. - person rici; 19.09.2019
comment
Ах ты прав! Мне все еще жаль ... Я изменил свой ответ! Большое спасибо! Приношу всем свои извинения. - person oneastok; 19.09.2019

Вам (не) повезло с вашим первым примером, потому что после деления с 131= в нем не было символов |.

Проблема здесь в том, что .* соответствует любой последовательности символов, включая любые символы вертикальной черты (|). Поэтому вам нужно исключить | из того, что вы сопоставляете. Итак, вместо .* используйте [^|]*

Кроме того, | может иметь особое значение, поэтому вам может потребоваться экранировать его (\|), если он не в скобках.

Но даже в этом случае вы не из леса. Дивизия 131=, очевидно, может перемещаться по линии. То есть, это может быть первым или может быть последним. Вы можете сделать его последним, просто удалив закрывающий |:

sed -e '/35=R/s/|131=[^|]*/|131=$UNIQUE/g' $f

(Я тестировал это с помощью поиска и замены Visual Studio, потому что он удобен, а sed - нет. Но он сделал то, что вы хотели.)

Чтобы взять случай, когда деление 131= может быть первым в строке, вам нужно добавить еще одно выражение:

sed -e '/35=R/s/|131=[^|]*/|131=$UNIQUE/g' -e '/35=R/s/^131=[^|]*/131=$UNIQUE/g' $f
person Spencer    schedule 18.09.2019
comment
Регулярные выражения Sed являются базовыми RE, если вы не вызываете sed с -E (или -r, устарело). Так же, как grep. Так что обратная косая черта | сделает его особенным. (Это, вероятно, не то же самое, что поиск и замена VS). - person rici; 18.09.2019
comment
@rici Это Visual Studio для вас. - person Spencer; 18.09.2019
comment
Я думаю, это скорее причуда sed; | означает, что альтернатива в наши дни довольно стандартна. Но старые утилиты, такие как sed и grep, используют BRE, в которых вы должны написать \|, если хотите чередовать; | - обычный персонаж. Значит, в этом вопросе ваш ответ неверен. - person rici; 19.09.2019
comment
@rici Я просто добавлю ласковые слова, если можно, чтобы охватить все возможности. - person Spencer; 19.09.2019
comment
До вас, но ласка все еще не так :-) TiO. запустить / ## LY1LC4JQEIX / - person rici; 19.09.2019