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, трябва да използвате \-екраниран действителен нов ред - изходната последователност \n не се поддържа там (за разлика от частта regex на повикването).

  • Или: просто вмъкнете действителен нов ред:

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

  • 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 не).
    Предупреждение: Въпреки че използването на -r / -E означава, че вашата команда по дефиниция е не съвместима с POSIX, все пак трябва да ограничите себе си към POSIX ERE (разширени регулярни изрази). За съжаление, това означава, че няма да можете да използвате няколко полезни конструкции, по-специално:

    • 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, приемете, че НЕ се поддържат управляващи последователности от контролни знаци, така че отново включете контролни знаци. като литерал, както по-горе.

      • 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?).
      • Не използвайте екраниране на контролен знак като \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 поддържа - като контейнер за stdin; използвайте -f /dev/stdin за преносимо четене на команди от stdin, включително от тук-документи (ако приемем, че вашата платформа поддържа /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
  • Изходни последователности

    • Екраниращи последователности, свързани със заместване, като \u в аргумента за заместване на функцията s///, които позволяват манипулация на подниз, в рамките на ограниченията; напр., sed 's/^./\u&/' <<<'dog' # -> 'Dog' - вижте http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command

    • Екраниращи последователности на контролни знаци: в допълнение към \n, \t, ..., екранирани кодови точки; например, всички следните екрани (шестнадесетични, осмични, десетични) представляват единични кавички ('): \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'']

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