Jak mogę poinstruować BSD sed, aby interpretował sekwencje specjalne, takie jak \ n i \ t?

14

Mam polecenie zamiany sed, które chciałbym być kompatybilne zarówno z BSD, sedjak i GNU sed. Rozszerzone wyrażenia regularne nie stanowią problemu, ponieważ nie potrzebuję ich w tym przypadku. Moim głównym problemem jest różnica w sposobie, w jaki oba sedinterpretują sekwencje specjalne znaków w ciągach zastępujących . Mój ciąg zastępujący zawiera tabulatory i znaki nowej linii i chciałbym, aby były one widoczne w ciągach poleceń dla ułatwienia konserwacji, jednak BSD sednie interpretuje sekwencji specjalnych i GNU sed tak . Jaki jest właściwy sposób instruowania, sedaby interpretować te sekwencje specjalne w BSD? Następujące dwa fragmenty są uosobieniem mojego problemu:

GNU ANTYLOPA sed

echo ABC | sed 's/B/\n\tB\n'

yeilds

A
    B
C

BSD sed

echo ABC | sed 's/B\n\tB\n'

daje

AntBnC

Oczywiście, \ni \tnie są interpretowane przez BSD jako sekwencje specjalnesed

A teraz moje pytanie. Według strony BSD sed:

Aby określić znak nowego wiersza w ciągu zastępującym, poprzedź go odwrotnym ukośnikiem.

Czy to oznacza, że ​​musiałbym poprzedzić dosłowny znak nowej linii odwrotnym ukośnikiem? Jaki jest właściwy sposób instruowania, sedaby interpretować sekwencje specjalne jak \nw tekście zastępczym?

ephsmith
źródło
2
BSD sed nie jest GNU sed i nie sądzę, że obsługuje takie ucieczki w danych wyjściowych. Musisz albo wstawić dosłowne znaki, zainstalować GNU sed, albo przełączyć się na coś, co obsługuje takie znaki ucieczki, jak awk.
jw013
@ jw013, jasno rozumiem różnicę między nimi. Instalacja GNU sed nie jest opcją. Miałem nadzieję, że uda mi się znaleźć między nimi wystarczająco dużo wspólnego, aby osiągnąć to, o co mi chodzi sed. W końcu prawdopodobnie będzie sensowne użycie awk. Co sądzisz o interpretacji cytowanej przeze mnie strony podręcznika BSD?
ephsmith
2
Tak, będziesz musiał używać dosłownych tabulatorów i znaków nowej linii, a w przypadku nowych linii musisz także poprzedzić je odwrotnym ukośnikiem, który jest po prostu mechanizmem kontynuacji linii.
jw013
@ jw013, dziękuję za wspaniałe odpowiedzi. W tym momencie, ze względu na utrzymanie, wezmę twoją radę i przerobię moje rozwiązanie w awk.
ephsmith
Dobry wybór - awk to znacznie lepszy plan niż obecnie akceptowana odpowiedź :)
jw013

Odpowiedzi:

6

Jeśli chcesz pisać przenośne skrypty, powinieneś trzymać się funkcji w standardzie POSIX (aka Single Unix aka Open Group Base Specification). Numer 7, znany również jako POSIX-1.2008, jest najnowszy, ale wiele systemów jeszcze go nie przyjęło. Wydanie 6, znane również jako POSIX-1.2001, jest zasadniczo dostarczane przez wszystkie współczesne jednorożce.

W sed , znaczenie sekwencje jak \ti \nnie jest przenośne, chyba że w regex , \nstoi za linią. W tekście zastępującym spolecenie \nnie jest przenośny, ale można użyć sekwencji odwrotnego ukośnika-nowa linia, aby zastąpić nową linię.

Przenośnym sposobem generowania znaku tabulacji (lub dowolnego innego znaku wyrażonego ósemkowo) jest tr. Przechowuj znak w zmiennej powłoki i podstaw tę zmienną we fragmencie sed.

tab=$(echo | tr '\n' '\t')
escape=$(echo | tr '\n' '\033')
embolden () {
  sed -e 's/^/'"$escape"'[1m/' -e 's/$/'"$escape"'[0m/'
}

Zauważ ponownie, że znaki nowej linii muszą być wyrażane inaczej w wyrażeniach regularnych i stekstach zastępczych.

Zamiast tego możesz użyć awk . Umożliwia ucieczki w odwrotnym ukośniku, w tym ósemkowe \ooo, w każdym dosłownym ciągu znaków.

Gilles „SO- przestań być zły”
źródło
7

Możesz użyć $'...'cudzysłowu, aby zinterpretować znaki ucieczki przed przekazaniem ciągu sed.

Ze strony podręcznika użytkownika bash:

   Words  of  the  form  $'string'  are  treated specially.  The word
   expands to string, with backslash-escaped characters  replaced  as
   specified  by the ANSI C standard.  Backslash escape sequences, if
   present, are decoded as follows:
          \a     alert (bell)
          \b     backspace
          \e     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \nnn   the eight-bit character whose  value  is  the  octal
                 value nnn (one to three digits)
          \xHH   the eight-bit character whose value is the hexadeci-
                 mal value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had
   not been present.

   A  double-quoted  string  preceded by a dollar sign ($) will cause
   the string to be translated according to the current  locale.   If
   the  current locale is C or POSIX, the dollar sign is ignored.  If
   the string is translated and replaced, the replacement is  double-
   quoted.
Kevin
źródło
3

Odpowiedź została udzielona w przypadku przepełnienia stosu:

/programming/1421478/how-do-i-use-a-new-line-replacement-in-a-bsd-sed

To prawie dokładnie to, co powiedział jw013.

Aby wstawić literalny tabulator, wpisz ctrl+ VTab.

bahamat
źródło
dzięki za referencje. Nienawidzę tego, że moje wyszukiwania w Google nie
zwróciły
1
Sugestia tabulatora Ctrl-V zależy od powłoki, na przykład nie będzie działać u ryb.
anddam
Nigdy nie korzystałem z ryb, nie byłem świadomy, ale dobrze wiedzieć.
bahamat