¿Cómo puedo instruir a BSD sed para que interprete secuencias de escape como \ n y \ t?

14

Tengo un comando de reemplazo de sed que me gustaría ser compatible con BSD sedy GNU sed. Las expresiones regulares extendidas no son un problema, ya que no las necesito en este caso. Mi problema principal es la diferencia en la forma en que los dos seds interpretan las secuencias de escape de caracteres en las cadenas de reemplazo . Mi cadena de reemplazo contiene pestañas y nuevas líneas y me gustaría que fueran visibles en las cadenas de comandos para facilitar el mantenimiento, sin embargo, BSD sedno interpreta las secuencias de escape y GNU sed . ¿Cuál es la forma adecuada de instruir sedpara interpretar estas secuencias de escape en BSD? Los siguientes dos fragmentos resumen mi problema:

ÑU sed

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

Yeilds

A
    B
C

BSD sed

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

rendimientos

AntBnC

Claramente, \ny \tno son interpretados como secuencias de escape por BSDsed

Ahora, a mi pregunta. Según la página de sedmanual de BSD :

Para especificar un carácter de nueva línea en la cadena de reemplazo, preceda con una barra diagonal inversa.

¿Esto implica que tendría que preceder una nueva línea literal por una barra diagonal inversa? ¿Cuál es la forma apropiada de instruir sedpara interpretar secuencias de escape como \nen el texto de reemplazo?

ephsmith
fuente
2
BSD sed no es GNU sed, y no creo que admita tales escapes en la salida. Tendrá que insertar caracteres literales, instalar GNU sed o cambiar a algo que admita escapes como awk.
jw013
@ jw013, tengo claro la diferenciación entre los dos. Instalar GNU sed no es una opción. Esperaba encontrar suficiente terreno común entre los dos para lograr lo que busco sed. Al final, probablemente tenga sentido usar awk. Entonces, ¿qué piensa sobre la interpretación de la página de manual de BSD sed que cité?
ephsmith
2
Sí, necesitará usar pestañas literales y nuevas líneas, y con las nuevas líneas también debe precederlas con una barra diagonal inversa, que es básicamente un mecanismo de continuación de línea.
jw013
@ jw013, gracias por sus excelentes respuestas. En este punto, por el bien del mantenimiento, seguiré su consejo y reelaboraré mi solución en awk.
ephsmith
Buena elección: awk es un plan mucho mejor que la respuesta actualmente aceptada :)
jw013

Respuestas:

6

Si necesita escribir scripts portátiles, debe atenerse a las características del estándar POSIX (también conocido como Single Unix, también conocido como Open Group Base Specification). El número 7, también conocido como POSIX-1.2008, es el último, pero muchos sistemas aún no han terminado de adoptarlo. El número 6, también conocido como POSIX-1.2001, es proporcionado en general por todos los dispositivos modernos.

En sed , el significado de secuencias de escape como \ty \nno es portátil, excepto que en una expresión regular , \nrepresenta una nueva línea. En el texto de reemplazo para un scomando, \nno es portátil, pero puede usar la secuencia barra invertida-nueva línea para representar una nueva línea.

Una forma portátil de generar un carácter de tabulación (o cualquier otro carácter expresado en octal) es con tr. Almacene el carácter en una variable de shell y sustitúyalo en el fragmento de sed.

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

Tenga en cuenta nuevamente que las nuevas líneas deben expresarse de manera diferente en expresiones regulares y en stextos de reemplazo.

Es posible que desee utilizar awk en su lugar. Permite escapes de barra diagonal inversa, incluidos escapes octales \ooo, en cada literal de cadena.

Gilles 'SO- deja de ser malvado'
fuente
7

Puede usar la $'...'cita bash para interpretar los escapes antes de pasar la cadena a sed.

Desde la página de manual de 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
fuente
3

Esto ha sido respondido en Stack Overflow:

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

Es casi exactamente lo que dijo jw013.

Para insertar una pestaña literal, escriba ctrl+ VTab.

bahamat
fuente
Gracias por la referencia. Odio que mis búsquedas en Google no devuelvan ese enlace: D
ephsmith
1
La sugerencia de la pestaña ctrl-V depende de la shell, por ejemplo, no funcionará en peces.
anddam
Como nunca había usado pescado, no lo sabía, pero es bueno saberlo.
bahamat