Чередование регулярных выражений / или оператор (foo | bar) в GNU или BSD Sed

28

Я не могу заставить его работать. Документация GNU sed говорит, что нужно убежать из трубы, но это не работает, равно как и использование прямой трубы без выхода. Добавление паренов не имеет значения.

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat|dog/Bear/g'
cat
dog
pear
banana
cat
dog

$ echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat\|dog/Bear/g'
cat
dog
pear
banana
cat
dog
Грегг Левенталь
источник

Ответы:

33

По умолчаниюsed используются базовые регулярные выражения POSIX , которые не содержат |оператора чередования. Многие версии sed, в том числе GNU и FreeBSD, поддерживают переключение на расширенные регулярные выражения , которые включают |чередование. То, как вы это делаете, варьируется: GNU sed использует-r , в то время как FreeBSD , NetBSD , OpenBSD и OS X sed используют -E. Другие версии в основном не поддерживают его вообще. Вы можете использовать:

echo 'cat dog pear banana cat dog' | sed -E -e 's/cat|dog/Bear/g'

и он будет работать на этих системах BSD и sed -rс GNU.


sedПохоже, что GNU имеет полностью недокументированную, но рабочую поддержку -E, поэтому, если у вас есть многоплатформенный скрипт, ограниченный вышеприведенным, это ваш лучший вариант. Так как это не задокументировано, вы, вероятно, не можете на него положиться.

В комментарии отмечается, что версии BSD также поддерживаются -rкак недокументированный псевдоним. OS X до сих пор не работает, и старые машины NetBSD и OpenBSD, к которым у меня есть доступ, тоже нет, но есть и NetBSD 6.1. Коммерческие Unices, которых я могу достичь повсеместно, не имеют. Таким образом, несмотря на все это, вопрос переносимости становится довольно сложным в данный момент, но простой ответ - переключиться на,awk если вам это нужно, который использует ERE повсюду.

Майкл Гомер
источник
Три BSDs вы упомянули весь каталог поддержка , оказываемая -rвариант как синоним -Eдля совместимости с GNU СЭД. OpenBSD и OS X sed -Eбудут интерпретировать экранированный канал как буквальный канал, а не как оператор чередования. Вот рабочая ссылка на справочную страницу NetBSD, а вот для OpenBSD, которой нет десяти лет.
Дэмиен
OSX поддерживает -E: developer.apple.com/library/mac/documentation/Darwin/Reference/…
Октавия Тогами
GNU sed поддерживает -E gnu.org/software/sed/manual/sed.html#index-_002dE .
Исаак
9

Это происходит потому, что (a|b)это расширенное регулярное выражение, а не простое регулярное выражение. Используйте -Eопцию, чтобы справиться с этим.

echo 'cat
dog
pear
banana
cat
dog'|sed -E 's/cat|dog/Bear/g'

Со sedстраницы руководства :

 -E      Interpret regular expressions as extended (modern) regular
         expressions rather than basic regular expressions (BRE's).

Обратите внимание, что -rэто еще один флаг для той же вещи, но -Eон более переносим и будет даже в следующей версии спецификаций POSIX.

Сетевик
источник
6

Портативный способ сделать это - и более эффективный способ - с помощью адресов. Ты можешь это сделать:

printf %s\\n cat dog pear banana cat dog |
sed -e '/cat/!{/dog/!b' -e '};cBear'

Таким образом, если строка не содержит строку cat и не содержит строку dog sed b , вышедшую из сценария, она автоматически печатает свою текущую строку и вытягивает следующую, чтобы начать следующий цикл. Поэтому он не выполняет следующую инструкцию - которая в этом примере cобрабатывает всю строку, чтобы прочитать Bear, но он может сделать что угодно.

Вероятно, стоит также отметить, что любой оператор, следующий за !bэтой sedкомандой, может совпадать только в строке, содержащей либо строку, dogлибо cat- так что вы можете выполнять дополнительные тесты без какой-либо опасности совпадения со строкой, которая не соответствует - что означает, что теперь вы можете применять правила только один или другой, а также.

Но это дальше. Вот вывод этой команды:

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

Вы также можете переносить таблицу поиска с обратными ссылками.

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ cat dog /;x
};G;s/^\(.*\)\n.* \1 .*/Bear/;P;d'

В этом простом примере можно настроить гораздо больше, но sedв долгосрочной перспективе это может привести к гораздо более гибким сценариям.

В первой строке I е xизменение трюм и шаблон затем вставить строку <space>кошки <space>собаки<space> в трюм до е xменяющихся их обратно.

С этого момента и в каждой следующей строке я Gи удерживаю пробел, добавленный к пробелу, затем проверяю, соответствуют ли все символы от начала строки до новой строки, которую я только что добавил в конце, строке, окруженной пробелами после нее. Если это так, я заменяю весь лот на Bear, а если нет, то это не причинит вреда, потому что я в следующий раз Pнабираю только до первой встречающейся новой строки в пространстве образца, затем dудаляю все это.

###OUTPUT###
Bear
Bear
pear
banana
Bear
Bear

И когда я говорю гибкий, я имею в виду это. Здесь он заменяет кошку на BrownBear и собаку на BlackBear :

printf %s\\n cat dog pear banana cat dog |
sed '1{x;s/^/ 1cat Brown 2dog Black /;x
};G;s/^\(.*\)\n.* [0-9]\1 \([^ ]*\) .*/\2Bear/;P;d'

###OUTPUT###
BrownBear
BlackBear
pear
banana
BrownBear
BlackBear

Конечно, вы можете значительно расширить содержание таблицы поиска - я взял идею из писем Грег Уббена по этому вопросу, когда в 90-х он описал, как он построил грубый калькулятор из одного sed s///утверждения.

mikeserv
источник
1
тьфу, +1. Я должен сказать, что у тебя есть склонность мыслить нестандартно
iruvar
@ 1_CR - см. Мое последнее редактирование - не моя идея - это не означает, что я не ценю это и считаю это комплиментом. Но я люблю отдавать должное, где это связано.
mikeserv
1

это довольно старый вопрос, но на тот случай, если кто-то захочет попробовать, есть довольно простой способ сделать это в sed с помощью sed-файлов. Каждый параметр может быть указан в отдельной строке, и sed оценит каждый из них. Это логический эквивалент или. Например, чтобы удалить строки, содержащие определенный код:

ты можешь сказать : sed -E '/^\/\*!(40103|40101|40111).*\/;$/d'

или поместите это в свой файл sed:

/^\/\*!40103.*\/;$/d
/^\/\*!40101.*\/;$/d
/^\/\*!40111.*\/;$/d
Мордехай
источник
0

Вот методика, которая не использует какие-либо специфические для реализации опции sed(например -E, -r). Вместо описания шаблона как одного регулярного выражения cat|dog, мы можем просто запустить sedдважды:

echo 'cat
dog
pear
banana
cat
dog' | sed 's/cat/Bear/g' | sed 's/dog/Bear/g'

Это очевидный обходной путь, но стоит поделиться. Естественно, это обобщает более чем две строки шаблона, хотя очень длинная цепочка sedне слишком хорошо выглядит.

Я часто использую sed -i(что работает одинаково во всех реализациях) для внесения изменений в файлы. Здесь может быть красиво включен длинный список строк шаблона, поскольку каждый временный результат сохраняется в файл:

for pattern in cat dog owl; do
    sed -i "s/${pattern}/Bear/g" myfile
done
jmd_dk
источник