Сед не дает мне правильную операцию замены для новой строки с Mac - различия между GNU sed и BSD / OSX sed

Я использую эту ссылку: sed help: сопоставление и замена литерала "\n" (не перевод строки)

и у меня есть файл "test1.txt", который содержит строку привет \ngoodbye

Я использую эту команду для поиска и замены "\ n" на новые символы новой строки:

sed -i '' 's/\\n/\n/g' test1.txt

но результат: чертовски до свидания. он просто заменяет "\ n" на "n", а не на новую строку. Это делает то же самое с / t, где он оставляет "t", а не вкладку.

'' для неопределенной ошибки в MAC: http://mpdaugherty.wordpress.com/2010/05/27/difference-with-sed-in-place-editing-on-mac-os-x-vs-linux/

Обновление:

Я попробовал обе команды, предложенные @hek2mgl:

sed -i 's/\\n/\n/g' test.txt
# Or:
sed -i'' 's/\\n/\n/g' test.txt

Хотя они могут работать с Linux, с MAC OS я получил следующую ошибку:

sed: 1: "test1.txt": undefined label 'est1.txt'

Не уверен, почему я не могу заставить это работать. Заранее спасибо.

2 ответа

Решение

С BSD / macOS sed , чтобы использовать новую строку в строке замены s вызов функции, вы должны использовать \ -экранированный фактический перевод строки -escape-последовательность \n там не поддерживается (в отличие от регулярных выражений вызова).

  • Либо: просто вставьте актуальный перевод строки:

    sed -i '' 's/\\n/\
    /g' test1.txt
    
  • Или: используйте строку ANSI C в кавычках ( $'...' ) соединить в новую строку ($'\n'; работает в bash, ksh, или же zsh):

    sed -i '' 's/\\n/\'$'\n''/g' test1.txt
    

GNU sed напротив, признает \n в замене строк; Продолжайте читать для всестороннего обзора различий между этими двумя реализациями.


Различия между GNU sed (Linux) и BSD / macOS sed

macOS использует версию BSD sed [1], который во многом отличается от GNU sed версия, которая поставляется с дистрибутивами Linux.

Их общим знаменателем является функциональность, определенная POSIX: см. POSIX sed спекуляция

Наиболее переносимым подходом является использование только функций POSIX, которые, однако, ограничивают функциональность:

  • Примечательно, что POSIX определяет поддержку только для базовых регулярных выражений, которые имеют множество ограничений (например, не поддерживают | (чередование) вообще, прямой поддержки нет + а также ?) и различные побег требования.
    • Предостережение: GNU sed (без -r ), поддерживает \| , \+ а также \? , который НЕ POSIX-совместимый; использование --posix отключить (см. ниже).
  • Чтобы использовать только функции POSIX:
    • (обе версии): используйте только -n а также -e варианты (в частности, не использовать -E или же -r включить поддержку расширенных регулярных выражений)
    • GNU sed: добавить опцию --posix для обеспечения функциональности только для POSIX (вам это не нужно строго, но без нее вы могли бы непреднамеренно использовать функции не-POSIX, не замечая этого; caveat: --posix сам по себе не POSIX-совместимый)
    • Использование функций только для POSIX означает более строгие требования к форматированию (отказ от многих удобств, доступных в GNU sed):
      • Последовательности управляющих символов, такие как \n а также \t как правило, НЕ поддерживаются.
      • Метки и команды ветвления (например, b) должен сопровождаться фактическим переводом строки или продолжением через отдельный -e вариант.
      • Смотрите ниже для деталей.

Однако в обеих версиях реализованы расширения стандарта POSIX:

  • Какие расширения они реализуют отличается (GNU sed реализует больше).
  • даже те расширения, которые они оба реализуют, частично отличаются по синтаксису.

Если вам нужна поддержка ОБА платформ (обсуждение различий):

  • Несовместимые функции:
    • Использование -i опция без аргумента (обновление на месте без резервного копирования) несовместима:
      • BSD sed: ДОЛЖЕН использовать -i ''
      • GNU sed: ДОЛЖЕН использовать только -i (Эквивалент: -i'') - с помощью -i '' не работает.
    • -i разумно включает нумерацию строк для каждого входного файла в GNU sed и последние версии BSD sed (например, во FreeBSD 10), но НЕ в macOS по состоянию на 10.12.
      Обратите внимание, что в отсутствие -i все версии номера строк в совокупности по входным файлам.
    • Если последняя строка ввода не имеет завершающей строки (и печатается):
      • BSD sed: всегда добавляет новую строку к выводу, даже если строка ввода не заканчивается на одну.
      • GNU sed: сохраняет статус завершающей новой строки, то есть добавляет новую строку, только если строка ввода заканчивается на одну.
  • Общие черты:
    • Если вы ограничиваете sed скрипты к чему BSD sed поддерживает, они вообще будут работать в GNU sed также - с заметным исключением использования специфичных для платформы расширенных функций регулярных выражений с -E, Очевидно, вы также откажетесь от расширений, характерных для версии GNU. Смотрите следующий раздел.

Рекомендации по кроссплатформенной поддержке (OS X/BSD, Linux), основанные на более строгих требованиях к версии BSD:

Обратите внимание, что я использую сокращенные MacOS и Linux для версий BSD и GNU sed соответственно потому что они стоковые версии на каждой платформе. Тем не менее, есть возможность установить GNU sed на macOS, например, используя Homebrew с brew install gnu-sed ,

Примечание: за исключением случаев, когда -r а также -E используются флаги (расширенные регулярные выражения), приведенные ниже инструкции сводятся к написанию POSIX-совместимых sed скрипты.

  • Для соответствия POSIX вы должны ограничить себя POSIX BRE (базовыми регулярными выражениями), которые, к сожалению, как следует из названия, довольно просты.
    Предостережение: не предполагайте, что \|, \+ а также \? поддерживаются: пока GNU sed поддерживает их (если --posix используется), BSD sed нет - эти функции не совместимы с POSIX.
    В то время как \+ а также \? можно эмулировать в POSIX-совместимом режиме:
    \{1,\} за \+,
    \{0,1\} за \?,
    \| (чередование) не может, к сожалению.
  • Для более мощных регулярных выражений используйте -E (скорее, чем -r) для поддержки ERE (расширенные регулярные выражения) (GNU sed не документирует -E, но это работает там как псевдоним -r; более новая версия BSD sed, такие как на FreeBSD 10, теперь также поддерживают -r, но версия macOS по состоянию на 10.10 нет).
    Будьте внимательны -r / -E означает, что ваша команда по определению не POSIX-совместимая, вы все равно должны ограничивать себя POSIX ERE (расширенными регулярными выражениями). К сожалению, это означает, что вы не сможете использовать несколько полезных конструкций, а именно:

    • словосочетания, потому что они зависят от платформы (например, \< в Linux, [[:<]] на OS X).
    • обратные ссылки внутри регулярных выражений (в отличие от обратных ссылок на совпадения групп захвата в строке замены s вызовы функций), потому что BSD sed не поддерживает их в расширенных регулярных выражениях (но, как ни странно, делает это в базовых регулярных выражениях, где они являются обязательными для POSIX).
  • Escape-последовательности управляющих символов, такие как \n а также \t :

    • В регулярных выражениях (как в шаблонах для выбора, так и в первом аргументе s функция), предположим, что только \n распознается как escape-последовательность (редко используется, так как пространство шаблона обычно представляет собой одну строку (без завершения \n), но не внутри класса символов, так что, например, [^\n] не работает; (если ваш ввод не содержит контрольных символов. кроме \t Вы можете подражать [^\n] с [[:print:][:blank:]]; в противном случае соедините контрольные символы. в качестве литералов [2]) - обычно включают управляющие символы в качестве литералов либо через встроенные строки ANSI C в кавычках (например, $'\t' ) в снарядах, которые его поддерживают ( bash, КШ, zsh ), или с помощью подстановок команд, используя printf (например, "$(printf '\t')" )
      • Только для Linux:
        sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
      • macOS и Linux:
        sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
        sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
    • В замене строк, используемых с s команда, предположим, что никакие управляющие последовательности управляющих символов не поддерживаются, поэтому снова включите управляющие символы. как литералы, как указано выше.

      • Только для Linux:
        sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b'
      • macOS и Linux:
        sed 's/-/'$'\t''/' <<<'a-b'
        sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
    • То же самое для текстовых аргументов i а также a функции: не используйте последовательности управляющих символов - см. ниже.

  • Метки и разветвление: метки, а также аргумент label-name для b а также t за функциями должен следовать буквальный перевод строки или врезка $'\n', В качестве альтернативы используйте несколько -e варианты и завершить каждый сразу после названия метки.
    • Только для Linux:
      sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
    • macOS и Linux:
      • ИЛИ (фактические новые строки):
        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'
  • функции i а также a для вставки / добавления текста: следуйте названию функции \ с последующим буквальным переводом строки или вставкой $'\n' перед указанием текстового аргумента.
    • Только для Linux:
      sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
    • macOS и Linux:
      sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
    • Замечания:
      • Без -e текстовый аргумент необъяснимо не завершается символом новой строки при выводе в macOS (ошибка?).
      • Не используйте экранированные символы, такие как \n а также \t в текстовом аргументе, так как они поддерживаются только в Linux.
      • Если текстовый аргумент имеет фактические внутренние переводы строк, \ -Сохраните их.
      • Если вы хотите поместить дополнительные команды после текстового аргумента, вы должны завершить его новой (неэкранированной) новой строкой (литеральной или врезанной) или продолжить с отдельной -e опция (это общее требование, которое применяется ко всем версиям).
  • Внутри списков функций (несколько вызовов функций заключены в {...}), обязательно завершите последнюю функцию перед закрытием } , с ;,
    • Только для Linux:
    • sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
    • macOS и Linux:
    • sed -n '1 {p;q;}' <<<$'a\nb'

GNU sed в BSD отсутствуют специфические особенности sed в целом:

Функции GNU, которые вы упустите, если вам потребуется поддержка обеих платформ:

  • Различные параметры регулярного выражения и замены (как в шаблонах для выбора строки, так и в первом аргументе s функция):

    • I опция для нечувствительного к регистру сопоставления регулярных выражений (невероятно, BSD sed не поддерживает это вообще).
    • M опция для многострочного соответствия (где ^ / $ сопоставить начало / конец каждой строки)
    • Для дополнительных опций, которые относятся к s функцию см. https://www.gnu.org/software/sed/manual/sed.html
  • Escape-последовательности

    • Связанные с замещением escape-последовательности, такие как \u в аргументе замены s/// функция, позволяющая манипулировать подстрокой в определенных пределах; например, sed 's/^./\u&/' <<<'dog' # -> 'Dog' - см. http://www.gnu.org/software/sed/manual/sed.html.

    • Escape-последовательности управляющих символов: в дополнение к \n, \t..., экранированные коды; например, все следующие экранированные символы (шестнадцатеричные, восьмеричные, десятичные) представляют одну кавычку ('): \x27, \o047, \d039 - см. https://www.gnu.org/software/sed/manual/sed.html

  • Расширения адресов, такие как 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'']

Это может показаться немного странным, но попробуйте:

sed -i '' 's/\\n/\
/g' test1.txt

Т.е. используйте актуальный перевод строки вместо \n,

Объяснение в том, что у вас странный sed! Подробности см. В руководстве по mac sed: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/sed.1.html

В описании s там команда говорит:

A line can be split by substituting a newline character into it.  To specify
a newline character in the replacement string, precede it with a backslash.

Также в описании -i опция говорит, что расширение не является обязательным, и что если вы не хотите его использовать, вы должны указать пустой аргумент. Так что все имеет смысл в конце концов!

Другие вопросы по тегам