sed
BSD / macOS Sed против GNU Sed против спецификации POSIX Sed
Поиск…
Вступление
Чтобы процитировать сообщение с @ SnoringFrog по созданию темы:
«Одна из самых больших ошибок при использовании sed - это сценарии, которые не работают (или преуспевают неожиданным образом), потому что они были написаны для одного, а не для другого. Простое сокращение более значительных различий было бы хорошим».
замечания
macOS использует версию BSD sed [1] , которая во многом отличается от версии GNU sed , поставляемой с дистрибутивами Linux .
Их общим знаменателем является функциональность, установленная POSIX : см. sed POSIX sed .
Самый портативный подход - использовать только функции POSIX , но, тем не менее, ограничивает функциональность :
В частности, POSIX определяет поддержку только для базовых регулярных выражений, которые имеют много ограничений (например, отсутствие поддержки
|(чередование) вообще, без прямой поддержки+и?) И различных требования вылетающих.- Оговорка: GNU
sed(без-r), не поддерживает\|,\+и\?, который НЕ соответствует POSIX; используйте--posixдля отключения (см. ниже).
- Оговорка: GNU
Использовать только функции POSIX :
(обе версии): используйте только параметры
-nи-e(в частности, не используйте-Eили-rчтобы включить поддержку расширенных регулярных выражений)GNU
sed: добавьте опцию--posixдля обеспечения только функции POSIX (вам это не нужно, но без нее вы можете случайно использовать функции, отличные от POSIX, не заметив, caveat :--posixсам по себе не совместим с POSIX )Использование только функций POSIX означает более жесткие требования к форматированию (из-за многих удобств, доступных в GNU
sed):- Такие последовательности символов управления, как
\nи\t, обычно НЕ поддерживаются. - Команды ярлыков и ветвлений (например,
b) должны сопровождаться фактической новой строкой или продолжением через отдельный параметр-e. - Подробнее см. Ниже.
- Такие последовательности символов управления, как
Однако в обеих версиях реализованы расширения для стандарта POSIX:
- какие расширения, которые они реализуют, различаются (GNU
sedреализует больше). - даже те расширения, которые они оба реализуют, частично отличаются синтаксисом .
Если вам нужна поддержка платформ BOTH (обсуждение различий):
Несовместимые функции:
Использование опции
-iбез аргумента (обновление на месте без резервного копирования) несовместимо:- BSD
sed: ДОЛЖНО использовать-i '' - GNU
sed: ДОЛЖНО использовать только-i(равнозначно:-i'') - использование-i ''НЕ работает.
- BSD
-iразумно включает строку нумерации строк ввода в GNUsedи последние версии BSDsed(например, на FreeBSD 10), но не имеет значения для macOS с 10.12 .
Обратите внимание, что в отсутствие-iвсех версий число строк кумулятивно отображается во входных файлах.Если последняя строка ввода не имеет завершающей новой строки (и печатается):
- BSD
sed: всегда добавляет новую строку на выходе, даже если строка ввода не заканчивается на один. - GNU
sed: сохраняет статус конечной новой строки , т. Е. Добавляет новую строку только в том случае, если строка ввода заканчивается на одном.
- BSD
Общие характеристики:
- Если вы ограничите свои сценарии
sedтем, что поддерживает BSDsed, они, как правило, будут работать и в GNUsed- с заметным исключением использования расширенных функций регулярного выражения, связанных с платформой, с-E. Очевидно, вы также откажетесь от расширений, характерных для версии GNU. См. Следующий раздел.
- Если вы ограничите свои сценарии
Рекомендации по кросс-платформенной поддержке (OS X / BSD, Linux), обусловленные более строгими требованиями версии BSD :
Обратите внимание, что сокращенные macOS и Linux иногда используются ниже, чтобы ссылаться на версии sed и BSD соответственно, потому что они являются версиями акций на каждой платформе. Тем не менее, можно установить GNU sed на macOS, например, используя Homebrew с brew install gnu-sed .
Примечание . За исключением случаев использования флагов -r и -E ( расширенные регулярные выражения), приведенные ниже инструкции относятся к написанию сценариев sed совместимых с POSIX .
Для соответствия POSIX вы должны ограничить себя POSIX BREs ( базовые регулярные выражения) , которые, к сожалению, как следует из названия, являются довольно базовыми.
Предостережение : не предполагайте, что\|,\+и\?поддерживаются: хотя GNUsedподдерживает их (если используется--posix), BSDsedне выполняет - эти функции не совместимы с POSIX.
В то время как\+и\?могут быть эмулированы в соответствии с POSIX:
\{1,\}для\+,
\{0,1\}для\?,
\|(чередование) не может, к сожалению.Для более мощных регулярных выражений используйте
-E(а не-r) для поддержки ERE ( расширенные регулярные выражения) (GNUsedне документирует-E, но он работает там как псевдоним-r; более новая версия BSDsed, например, на FreeBSD 10, теперь также поддерживают-r, но версия MacOS от 10.12 не делает).
Предостережение . Несмотря на то, что использование-r/-Eозначает, что ваша команда по определению не совместима с POSIX, вы все равно должны ограничивать себя POSIX ERE (расширенные регулярные выражения) . К сожалению, это означает, что вы не сможете использовать несколько полезных конструкций, в частности:- потому что они специфичны для платформы (например,
\<на Linux,[[:<]]на OS X). - обратные ссылки внутри регулярных выражений (в отличие от «обратных ссылок» на совпадения записей в заменяющей строке вызовов функций
s), поскольку BSDsedне поддерживает их в расширенных регулярных выражениях (но, что любопытно, делает это в базовые , где они предназначены для POSIX).
- потому что они специфичны для платформы (например,
Управляющие символы, такие как
\nи\t:В регулярных выражениях (как в шаблонах для выбора линии, так и в первом аргументе функции
s) предположим, что только\nраспознается как escape-последовательность (редко используется, поскольку пространство шаблонов обычно представляет собой одну строку (без завершения\n) но не внутри символьного класса , так что, например,[^\n]не работает; (если ваш вход не содержит управляющих символов, кроме\t, вы можете эмулировать[^\n]с помощью[[:print:][:blank:]], в противном случае - символы управления соединением в виде литералов [2] ) - обычно включают контрольные символы в виде литералов , либо с помощью связанных строк в ANSI C (например,$'\t') в оболочек, поддерживающих его (bash,ksh,zsh), или с помощью подстановок команд с помощьюprintf(например,"$(printf '\t')") .- Только Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b' - OSX и Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- Только Linux:
В строках замещения, используемых с командой
s, предположим, что NO управляющих последовательностей escape-символов поддерживается , так что опять же включите контрольные символы. как литералы , как указано выше.- Только Linux:
sed 's/-/\t/' <<<$'ab' # -> 'a<tab>b' - macOS и Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- Только Linux:
То же самое для текстовых аргументов
iиaфункции: не использовать последовательности управления символьные - смотри ниже.
Ярлыки и ветвление : метки, а также аргумент имени метки для функций
bиtдолжны сопровождаться либо литеральной новой линией, либо сплайсинговым в$'\n'. Кроме того, используйте несколько опций-eи завершайте их сразу после имени метки.- Только Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a' - macOS и Linux:
- EITHER (фактические строки новой строки):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb' - ИЛИ (вложенные экземпляры
$\n):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb' - ИЛИ (несколько
-eопций):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- EITHER (фактические строки новой строки):
- Только Linux:
Функции
iиaдля вставки / добавления текста : следуйте за именем функции по\, за которым следует либо буквальная новая строка, либо сплайсинг в$'\n'прежде чем указывать текстовый аргумент.- Только Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b' - OSX и Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb' - Замечания:
- Без
-eтекстовый аргумент необъяснимо не завершен с использованием новой строки при выводе на macOS (ошибка?). - Не используйте escape-символы, такие как
\nи\tв текстовом аргументе, поскольку они поддерживаются только в Linux. - Если текстовый аргумент имеет фактические внутренние символы новой строки,
\-escape их. - Если вы хотите поместить дополнительные команды после текстового аргумента, вы должны закончить его с помощью (неэкспертированной) новой строки (будь то литеральный или сращиваемый) или продолжить с отдельной опцией
-e(это общее требование, которое применяется ко всем версиям) ,
- Без
- Только Linux:
Внутри списков функций (несколько вызовов функций, заключенных в
{...}), обязательно завершайте последнюю функцию перед закрытием};,- Только Linux:
-
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
-
- macOS и Linux:
-
sed -n '1 {p;q;}' <<<$'a\nb'
-
- Только Linux:
GNU sed -специальные функции, отсутствующие в BSD sed :
Возможности GNU вы пропустите, если вам нужно поддерживать обе платформы:
Различные варианты соответствия и замены регулярных выражений (как в шаблонах для выбора линии, так и в первом аргументе функции
s):- Параметр
Iдля сопоставления регулярных выражений case-INsensitive (невероятно, BSDsedне поддерживает это вообще). - Параметр
Mдля многострочного совпадения (где^/$соответствует началу / концу каждой строки ) - Дополнительные параметры, относящиеся к функции
s, см. В https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- Параметр
Эквивалентные последовательности
Вспомогательные escape-последовательности, такие как
\uв аргументе замены функцииs///которые допускают манипуляции подстрокой , в пределах; например,sed 's/^./\u&/' <<<'dog' # -> 'Dog'- см. http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022 -commandУправляющие последовательности escape-последовательностей: в дополнение к
\n,\t, ..., escape-пунктам на основе кода; например, все следующие escape-последовательности (шестнадцатеричные, восьмеричные, десятичные) представляют одну цитату ('):\x27,\o047,\d039- см. https://www.gnu.org/software/sed/manual/ sed.html # Escapes
Расширения адресов , такие как
first~stepчтобы соответствовать каждой шаг-й строке,addr, +Nчтобы соответствовать N строкам послеaddr, ... - см. Http://www.gnu.org/software/sed/manual/sed. HTML # Адреса
[1] Версия macOS sed старше версии на других BSD-подобных системах, таких как FreeBSD и PC-BSD. К сожалению, это означает, что вы не можете предположить, что функции, которые работают в FreeBSD, например, будут работать [одинаково] на macOS.
[2] Строка с цитированием ANSI C $'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177' содержит все управляющие символы ASCII, кроме \n (и NUL), поэтому вы можете использовать его в сочетании с [:print:] для довольно надежной эмуляции [^\n] :
'[[:print:]'$'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177'']
Замените все символы новой строки на вкладки
Примечание. Для краткости команды используют здесь строки ( <<< ) и строки с котировкой ANSI C ( $'...' ) . Обе эти функции оболочки работают в bash , ksh и zsh .
# GNU Sed
$ sed ':a;$!{N;ba}; s/\n/\t/g' <<<$'line_1\nline_2\nline_3'
line_1 line_2 line_3
# BSD Sed equivalent (multi-line form)
sed <<<$'line_1\nline_2\nline_3' '
:a
$!{N;ba
}; s/\n/'$'\t''/g'
# BSD Sed equivalent (single-line form, via separate -e options)
sed -e ':a' -e '$!{N;ba' -e '}; s/\n/'$'\t''/g' <<<$'line 1\nline 2\nline 3'
BSD Sed отмечает:
Обратите внимание на необходимость прекращения меток (
:a) и команд ветвления (ba) либо с фактическими символами новой строки, либо с отдельными параметрами-e.Поскольку контрольно-символьные управляющие последовательности , такие как
\tне поддерживаются в строке замены, вкладка ANSI C цитируемой буквальная сплайсируются в строку замены.
(В регулярных выражениях части, BSD Sed распознает только\nв управляющей последовательности).
Добавить литерал в строку с функцией 'a'
Примечание. Для краткости команды используют здесь строки ( <<< ) и строки с котировкой ANSI C ( $'...' ) . Обе эти функции оболочки работают в bash , ksh и zsh .
# GNU Sed
$ sed '1 a appended text' <<<'line 1'
line 1
appended text
# BSD Sed (multi-line form)
sed '1 a\
appended text' <<<'line 1'
# BSD Sed (single-line form via a Bash/Ksh/Zsh ANSI C-quoted string)
sed $'1 a\\\nappended text' <<<'line 1'
Обратите внимание, что для BSD Seed требуется \ последующей фактической новой строкой для добавления добавляемого текста.
То же самое относится к связанным функциям i (insert) и c (delete and insert).