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

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

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

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

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

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

the '' для ошибки undefined в 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'

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

Ответ 1

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

  • Либо: просто вставьте фактическую строку новой строки:

    sed -i '' 's/\\n/\
    /g' test1.txt
    
  • Или: используйте ANSI C-quoted string ($'...') для сращивания в новой строке ($'\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 реализует больше).
  • даже те расширения, которые они оба реализуют, частично отличаются синтаксисом.

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

  • Несовместимые функции:
    • Использование параметра -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 BREs (основные регулярные выражения), которые, к сожалению, как следует из названия, вполне основной.
    Предостережение: не предполагайте, что поддерживаются \|, \+ и \?: в то время как 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 EREs (расширенные регулярные выражения). К сожалению, это означает, что вы не сможете использовать несколько полезных конструкций, в частности:

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

    • В регулярных выражениях (как в шаблонах для выбора, так и в первом аргументе функции s) предположим, что только \n распознается как escape-последовательность (редко используется, так как пространство шаблонов как правило, представляет собой одну строку (без завершения \n), но не внутри символьного класса, так что, например, [^\n] не работает; (если ваш вход не содержит контрольных символов, кроме \t, вы можете эмулировать [^\n] с помощью [[:print:][:blank:]], в противном случае символы управления соединением в виде литералов [2]) - в общем случае включают контрольные символы в виде литералов либо через сплайсинговый ANSI C-quoted строки (например, $'\t') в оболочках, которые поддерживают его (bash, ksh, 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, предположим, что NO escape-последовательности управляющего символа поддерживаются, поэтому снова включите контрольные символы. как литералы, как указано выше.

      • Только Linux:
        sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b'
      • macOS и Linux:
        sed 's/-/'$'\t''/' <<<'a-b'
        sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
    • То же самое для текстовых аргументов для 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'
  • Функции 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 (ошибка?).
      • Не используйте escape-символы esc \n и \t в текстовом аргументе, поскольку они поддерживаются только в Linux.
      • Если текстовый аргумент имеет фактические внутренние символы новой строки, \ -escape them.
      • Если вы хотите разместить дополнительные команды после текстового аргумента, вы должны завершить его с помощью строки (неэкспертированной) новой строки (будь то литеральная или объединенная) или продолжить с помощью отдельной опции -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 для сопоставления регулярных выражений case-INsensitive (невероятно, BSD sed не поддерживает это вообще).
    • Параметр M для многострочного сопоставления (где ^/$ соответствует началу/концу каждой строки)
    • Дополнительные параметры, относящиеся к функции s, см. в https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
  • Экранирующие последовательности

    • Управляющие последовательности, связанные с заменой, такие как \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/sed/manual/sed.html#Addresses


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

Ответ 2

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

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

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