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

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

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

Я использую эту команду для поиска и замены "\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'

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


person Brandon Ling    schedule 17.06.2014    source источник
comment
Я попробовал то же самое в Linux (в настоящее время в Ubuntu), и у меня это работает с sed -i 's/\\n/\n/g' input.txt.   -  person McLovin    schedule 18.06.2014
comment
Сообщение об ошибке означает, что он интерпретирует строку test1.txt как сценарий sed, где t — это команда, условный переход к метке с именем ext1.txt, которой, конечно же, не существует. Странно, что пустой аргумент необходим, но я думаю, что это отвлекающий маневр по отношению к проблеме с новой строкой.   -  person ooga    schedule 18.06.2014


Ответы (2)


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

  • Ибо: просто вставьте фактический перевод строки:

    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], которая во многих отношениях отличается от версии sed GNU, поставляется с дистрибутивами Linux.

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

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

  • Notably, POSIX specifies support only for basic regular expressions, which have many limitations (e.g., no support for | (alternation) at all, no direct support for + and ?) and different escaping requirements.
    • Caveat: GNU sed (without -r), does support \|, \+ and \?, which is NOT POSIX-compliant; use --posix to disable (see below).
  • To use POSIX features only:
    • (both versions): use only the -n and -e options (notably, do not use -E or -r to turn on support for extended regular expressions)
    • GNU sed: добавьте параметр --posix, чтобы обеспечить функциональность только для POSIX (это не обязательно, но без него вы можете непреднамеренно использовать функции, отличные от POSIX, не заметив этого; предостережение: --posix сам не POSIX-совместимый)
    • Using POSIX-only features means stricter formatting requirements (forgoing many conveniences available in GNU sed):
      • Control-character sequences such as \n and \t are generally NOT supported.
      • Метки и команды ветвления (например, b) должны сопровождаться фактической новой строкой или продолжением с помощью отдельной опции -e.
      • Подробнее см. ниже.

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

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

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

  • Incompatible features:
    • Use of the -i option without an argument (in-place updating without backup) is incompatible:
      • BSD sed: MUST use -i ''
      • GNU sed: НЕОБХОДИМО использовать только -i (эквивалент: -i'') — использование -i '' НЕ работает.
    • -i разумно включает нумерацию строк для каждого входного файла в GNU sed и последних версиях BSD sed (например, во FreeBSD 10), но НЕ работает в macOS версии 10.15.
      Обратите внимание, что при отсутствии -i все строки номеров версий кумулятивно во входных файлах.
    • If the last input line does not have a trailing newline (and is printed):
      • BSD sed: always appends a newline on output, even if the input line doesn't end in one.
      • GNU sed: сохраняет статус завершающей новой строки, т. е. добавляет новую строку, только если входная строка заканчивается единицей.
  • Common features:
    • If you restrict your sed scripts to what BSD sed supports, they will generally work in GNU sed too - with the notable exception of using platform-specific extended regex features with -E. Obviously, you'll also forgo extensions that are specific to the GNU version. See next section.

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

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

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

  • Для соответствия POSIX вы должны ограничиться стандартами POSIX BRE (базовые< /em> регулярные выражения), которые, к сожалению, как следует из названия, довольно просты.
    Предостережение: не думайте, что поддерживаются \|, \+ и \?: в то время как 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 нет em>).
    Предостережение. Несмотря на то, что использование -r / -E означает, что ваша команда по определению не совместима с POSIX, вы все равно должны ограничивать себя в ERE POSIX (расширенные регулярные выражения). К сожалению, это означает, что вы не сможете использовать несколько полезных конструкций, в частности:

    • word-boundary assertions, because they're platform-specific (e.g., \< on Linux, [[:<]] on OS X).
    • обратные ссылки внутри регулярных выражений (в отличие от «обратных ссылок» на совпадения группы захвата в строке замены s вызовов функций), поскольку BSD sed не поддерживает их в расширенные регулярные выражения (но, что любопытно, делает то же самое в базовых, где они обязательны для POSIX).
  • Экран-последовательности управляющих символов, такие как \n и \t:

    • In regexes (both in patterns for selection and the first argument to the s function), assume that only \n is recognized as an escape sequence (rarely used, since the pattern space is usually a single line (without terminating \n), but not inside a character class, so that, e.g., [^\n] doesn't work; (if your input contains no control chars. other than \t, you can emulate [^\n] with [[:print:][:blank:]]; otherwise, splice control chars. in as literals[2]) - generally, include control characters as literals, either via spliced-in ANSI C-quoted strings (e.g., $'\t') in shells that support it (bash,ksh, zsh), or via command substitutions using printf (e.g., "$(printf '\t')").
      • Linux only:
        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, предполагается, что escape-последовательности управляющих символов НЕ поддерживаются, поэтому снова включите управляющие символы. как литералы, как указано выше.

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

  • Labels and branching: labels as well as the label-name argument to the b and t functions must be followed by either by a literal newline or a spliced-in $'\n'. Alternatively, use multiple -e options and terminate each right after the label name.
    • Linux only:
      sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
    • macOS and Linux:
      • EITHER (actual newlines):
        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'
  • Functions i and a for inserting/appending text: follow the function name by \, followed either by a literal newline or a spliced-in $'\n' before specifying the text argument.
    • Linux only:
      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'
    • Note:
      • Without -e, the text argument is inexplicably not newline-terminated on output in macOS (bug?).
      • Не используйте escape-символы, такие как \n и \t, в текстовом аргументе, так как они поддерживаются только в Linux.
      • Поэтому, если текстовый аргумент имеет фактические внутренние символы новой строки, \-экранируйте их.
      • Если вы хотите поместить дополнительные команды после текстового аргумента, вы должны завершить его (неэкранированной) новой строкой (будь то литеральной или вставленной) или продолжить с отдельной опцией -e (это общее требование, применимое ко всем версиям).
  • Внутри функций, списков (несколько вызовов функций, заключенных в {...}), не забудьте также завершить последнюю функцию перед закрытием }, с ;.

    • Linux only:
    • sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
    • macOS и Linux:
    • sed -n '1 {p;q;}' <<<$'a\nb'
  • С опцией -f (для чтения команд из файла) только GNU sed поддерживает - в качестве заполнителя для стандартного ввода; используйте -f /dev/stdin для переносимого чтения команд из стандартного ввода, в том числе из here-documents (при условии, что ваша платформа поддерживает /dev/stdin, что обычно имеет место в настоящее время).


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

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

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

    • The I option for case-INsensitive regex matching (incredibly, BSD sed doesn't support this at all).
    • Параметр 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

    • Эскейп-последовательности управляющих символов: в дополнение к \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


[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'']

person mklement0    schedule 18.06.2014
comment
+1 Отличный ответ. Я просто блуждал в темноте. - person ooga; 18.06.2014
comment
Ну, ты не один бродил... отличный ответ, спасибо. - person vikingsteve; 12.11.2018

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

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 говорится, что расширение не является необязательным, и что если оно вам не нужно, вы должны указать пустой аргумент. Так что в конце концов все логично!

person ooga    schedule 18.06.2014
comment
Я получаю эту ошибку: sed: 1: s/\\n//g: неэкранированная новая строка внутри шаблона замены - person Brandon Ling; 18.06.2014
comment
Я отредактировал это. Попробуйте еще раз (с обратной косой чертой перед новой строкой). - person ooga; 18.06.2014
comment
Это сработало! : D, можешь объяснить в своем ответе? и, возможно, почему он не работает должным образом с MAC, а не с Linux? - person Brandon Ling; 18.06.2014
comment
@BrandonLing См. редактирование. - person ooga; 18.06.2014