sed no me proporciona una operación sustituta correcta para la nueva línea con Mac: diferencias entre GNU sed y BSD/OSX sed [duplicado]

Resuelto Brandon Ling asked hace 10 años • 2 respuestas

Estoy usando esta referencia: ayuda sed: hacer coincidir y reemplazar un literal "\n" (no la nueva línea)

y tengo un archivo "test1.txt" que contiene una cadena hola\nadiós

Utilizo este comando para buscar y reemplazar "\n" con caracteres de nueva línea reales:

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

pero el resultado es: hola, adiós . simplemente reemplaza "\n" con "n" y no una nueva línea real. Esto hace lo mismo con /t donde dejará una "t" y no una pestaña.

el '' es para el error indefinido en MAC: http://mpdaugherty.wordpress.com/2010/05/27/difference-with-sed-in-place-editing-on-mac-os-x-vs-linux /

Actualizar :

Probé los dos comandos que sugirió @ hek2mgl:

sed -i 's/\\n/\n/g' test.txt
# Or:
sed -i'' 's/\\n/\n/g' test.txt

Si bien es posible que funcionen con Linux, con MAC OS recibí el siguiente error:

sed: 1: "test1.txt": undefined label 'est1.txt'

No estoy seguro de por qué no puedo hacer que esto funcione. Gracias de antemano.

Brandon Ling avatar Jun 18 '14 06:06 Brandon Ling
Aceptado

Con BSD/macOS sed, para usar una nueva línea en la cadena de reemplazo de una sllamada de función, debe usar una nueva línea real\ con escape ; la secuencia de escape no\n se admite allí (a diferencia de la parte de expresión regular de la llamada).

  • Ya sea : simplemente inserte una nueva línea real :

    sed -i '' 's/\\n/\
    /g' test1.txt
    
  • O : utilice una cadena ANSI entre comillas C ( $'...') para empalmar la nueva línea ( $'\n'; funciona en bash, ksho zsh):

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

GNU sed , por el contrario, reconoce \ncadenas de reemplazo; Siga leyendo para obtener una descripción general completa de las diferencias entre estas dos implementaciones.


Diferencias entre GNU sed(Linux) y BSD/macOSsed

macOS usa la versión BSD de sed[1] , que difiere en muchos aspectos de la versión GNU sed que viene con las distribuciones de Linux .

Su denominador común es la funcionalidad decretada por POSIX : consulte la especificación POSIX .sed

El enfoque más portátil es utilizar únicamente funciones POSIX , lo que, sin embargo, limita la funcionalidad :

  • En particular, POSIX especifica soporte solo para expresiones regulares básicas , que tienen muchas limitaciones (por ejemplo, no admiten |(alternancia) en absoluto, no admiten directamente +y ?) y diferentes requisitos de escape.
    • Advertencia: GNU sed(sin -r), admite\| y \+, que NO es compatible con \?POSIX; Úselo --posixpara desactivar (ver más abajo).
  • Para utilizar únicamente las funciones POSIX :
    • (ambas versiones): use solo las opciones -ny -e(en particular, no use o -Eactive -rla compatibilidad con expresiones regulares extendidas )
    • GNU sed: agregue una opción --posixpara garantizar la funcionalidad solo de POSIX (no es estrictamente necesario, pero sin ella podría terminar usando sin darse cuenta funciones que no son POSIX sin darse cuenta; advertencia : --posix en sí mismo no es compatible con POSIX)
    • El uso de funciones exclusivas de POSIX implica requisitos de formato más estrictos (renunciando a muchas comodidades disponibles en GNU sed):
      • Las secuencias de caracteres de control como \ny \tgeneralmente NO son compatibles.
      • Las etiquetas y los comandos de bifurcación (por ejemplo, b) deben ir seguidos de una nueva línea real o una continuación a través de una opción separada -e.
      • Consulte a continuación para obtener más detalles.

Sin embargo, ambas versiones implementan extensiones al estándar POSIX:

  • las extensiones que implementan difieren (GNUsedimplementa más).
  • incluso aquellas extensiones que ambos implementan difieren parcialmente en la sintaxis .

Si necesita admitir AMBAS plataformas (discusión de diferencias):

  • Funciones incompatibles :
    • El uso de la -iopción sin argumento (actualización local sin copia de seguridad) es incompatible:
      • BSD sed: DEBE usarse-i ''
      • GNU sed: DEBE usar solo -i(equivalente:) -i''; el uso -i ''NO funciona.
    • -iactiva con sensatez la numeración de líneas por archivo de entrada en GNU sed y versiones recientes de BSD sed (por ejemplo, en FreeBSD 10), pero NO lo hace en macOS a partir de 10.15 .
      Tenga en cuenta que, en ausencia de -i todas las versiones, las líneas numéricas se acumulan en los archivos de entrada.
    • Si la última línea de entrada no tiene una nueva línea al final (y se imprime):
      • BSD sed: siempre agrega una nueva línea en la salida, incluso si la línea de entrada no termina en una.
      • GNU sed: conserva el estado de nueva línea final , es decir, añade una nueva línea sólo si la línea de entrada termina en una.
  • Características comunes :
    • Si restringe sus sedscripts a lo que admite BSD sed, generalmente también funcionarán en GNU sed, con la notable excepción del uso de funciones de expresiones regulares extendidas-E específicas de la plataforma con . Obviamente, también renunciará a las extensiones específicas de la versión GNU. Consulte la siguiente sección.

Directrices para soporte multiplataforma (macOS/BSD, Linux), impulsadas por los requisitos más estrictos de la versión BSD :

Tenga en cuenta que estoy usando las abreviaturas macOS y Linux para las versiones BSD y GNU respectivamente sedporque son las versiones estándar en cada plataforma. Sin embargo, es posible instalar GNU seden macOS, por ejemplo, usando Homebrew con brew install gnu-sed.

Nota : Excepto cuando se utilizan los indicadores -ry-E ( expresiones regulares extendidas ), las instrucciones siguientes equivalen a escribir scripts compatibles con POSIX . sed

  • Para cumplir con POSIX, debe limitarse a POSIX BRE ( expresiones regulares básicas ) , que, desafortunadamente, como sugiere el nombre, son bastante básicas.
    Advertencia : no asuma que \|, \+y \?son compatibles: si bien GNU sedlos admite (a menos --posixque se use), BSD sedno; estas características no son compatibles con POSIX.
    Mientras que \+y se \? puede emular de manera compatible con POSIX:
    \{1,\}for \+,
    \{0,1\}for \?,
    \|(alternación) no , desafortunadamente.
  • Para expresiones regulares más potentes, utilice-E (en lugar de -r) para admitir ERE ( expresiones regulares extendidas ) (GNU sedno documenta -E, pero funciona allí como un alias de -r; la versión más reciente de BSD sed, como en FreeBSD 10, ahora también admite -r, pero la versión de macOS a partir de 10.10 no ).
    Advertencia : aunque el uso de -r/ -Esignifica que su comando, por definición, no es compatible con POSIX, aún debe restringirse a POSIX ERE (expresiones regulares extendidas) . Lamentablemente, esto significa que no podrá utilizar varias construcciones útiles, en particular:

    • aserciones de límites de palabras, porque son específicas de la plataforma (por ejemplo, \<en Linux, [[:<]]en OS X).
    • referencias hacia atrás dentro de expresiones regulares (a diferencia de las "referencias hacia atrás" para capturar coincidencias de grupos en la cadena de reemplazo de sllamadas a funciones), porque BSD sedno las admite en expresiones regulares extendidas (pero, curiosamente, sí lo hace en las básicas ). , donde tienen mandato POSIX).
  • Secuencias de escape de caracteres de control como \ny \t:

    • En las expresiones regulares (tanto en los patrones de selección como en el primer argumento de la sfunción), supongamos que solo \nse reconoce como una secuencia de escape (rara vez se usa, ya que el espacio del patrón suele ser una sola línea (sin terminar \n), pero no dentro de una clase de carácter. , de modo que, por ejemplo, [^\n]no funciona; (si su entrada no contiene caracteres de control distintos de \t, puede emular [^\n]con [[:print:][:blank:]]; de lo contrario, empalme los caracteres de control como literales [2] ) - generalmente, incluya caracteres de control como literales , ya sea mediante cadenas ANSI C entrecomilladas (por ejemplo, $'\t') en shells que lo admiten ( bash,ksh, zsh), o mediante sustituciones de comandos usandoprintf (por ejemplo, "$(printf '\t')") .
      • Sólo Linux:
        sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
      • MacOS y Linux:
        sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
        sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
    • En las cadenas de reemplazo utilizadas con el scomando, suponga que NO se admiten secuencias de escape de caracteres de control , por lo que, nuevamente, incluya caracteres de control. como literales , como arriba.

      • Sólo Linux:
        sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b'
        sed 's/-/\n/' <<<$'a-b' # -> 'a<newline>b'
      • macOS y Linux:
        sed 's/-/'$'\t''/' <<<'a-b'
        sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
        sed 's/-/\'$'\n''/' <<<'a-b'
        tenga en cuenta que las nuevas líneas deben tener una barra invertida para que se interpreten correctamente como parte de la cadena de reemplazo y no como el final del comando, y que el uso printfno funciona para las nuevas líneas ya que las nuevas líneas finales se eliminan mediante sustituciones de comandos. ( $(...)).
    • Lo mismo ocurre con los argumentos de texto de las funciones iya : no utilice secuencias de caracteres de control ; consulte a continuación.

  • Etiquetas y bifurcaciones : las etiquetas, así como el argumentob del nombre de la etiqueta de las tfunciones y deben ir seguidos de una nueva línea literal o de un empalme$'\n' . Alternativamente, use múltiples -eopciones y termine cada una justo después del nombre de la etiqueta.
    • Sólo Linux:
      sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
    • MacOS y Linux:
      • CUALQUIERA (nuevas líneas reales):
        sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb'
      • O (instancias empalmadas $\n):
        sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb'
      • O (múltiples -eopciones):
        sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
  • Funciones iy apara insertar/añadir texto : siga el nombre de la función por \, seguido de una nueva línea literal o un empalme$'\n' antes de especificar el argumento de texto.
    • Sólo Linux:
      sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
    • MacOS y Linux:
      sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
    • Nota:
      • Sin -e, el argumento de texto inexplicablemente no termina en una nueva línea en la salida en macOS (¿error?).
      • No utilice escapes de caracteres de control como \ny \ten el argumento de texto, ya que solo se admiten en Linux.
      • Por lo tanto, si el argumento del texto tiene nuevas líneas interiores reales, \escápelas.
      • Si desea colocar comandos adicionales después del argumento de texto, debe terminarlo con una nueva línea (sin escape) (ya sea literal o intercalada) o continuar con una -eopción separada (este es un requisito general que se aplica a todas las versiones).
  • Dentro de las listas de funciones (múltiples llamadas a funciones encerradas en {...}), asegúrese de finalizar también la última función, antes del cierre }, con; .

    • Sólo Linux:
    • sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
    • MacOS y Linux:
    • sed -n '1 {p;q;}' <<<$'a\nb'
  • Con la -fopción (para leer comandos de un archivo), solo GNU sedadmite -como marcador de posición para stdin; utilícelo -f /dev/stdinpara leer comandos de forma portátil desde stdin , incluidos los de here-documents (suponiendo que su plataforma lo admita /dev/stdin, lo que suele ser el caso hoy en día).


sedCaracterísticas específicas de GNU que faltan por completo en BSD sed:

Funciones de GNU que se perderá si necesita admitir ambas plataformas:

  • Varias opciones de sustitución y coincidencia de expresiones regulares (tanto en patrones para la selección de línea como en el primer argumento de la sfunción):

    • La Iopción para la coincidencia de expresiones regulares que distinguen entre mayúsculas y minúsculas (increíblemente, BSD sedno admite esto en absoluto).
    • La Mopción de coincidencia de varias líneas (donde ^/ $coincide con el inicio/final de cada línea )
    • Para opciones adicionales que son específicas de la sfunción, consulte https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
  • Secuencias de escape

    • Secuencias de escape relacionadas con la sustitución, como \uen el argumento de reemplazo de la s///función, que permiten la manipulación de subcadenas , dentro de límites; por ejemplo, sed 's/^./\u&/' <<<'dog' # -> 'Dog'consulte http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command

    • Secuencias de escape de caracteres de control: además de \n, \t, ..., escapes basados ​​en puntos de código; por ejemplo, todos los siguientes escapes (hex., octal, decimal) representan una comilla simple ( '): \x27, \o047, \d039- consulte https://www.gnu.org/software/sed/manual/sed.html#Escapes

  • Extensiones de dirección , como first~steppara hacer coincidir cada paso de línea, addr, +Npara hacer coincidir N líneas siguientes addr, ... - consulte http://www.gnu.org/software/sed/manual/sed.html#Addresses


[1] La sedversión de macOS es anterior a la versión de otros sistemas similares a BSD, como FreeBSD y PC-BSD. Desafortunadamente, esto significa que no se puede asumir que las características que funcionan en FreeBSD, por ejemplo, funcionarán [igualmente] en macOS.

[2] La cadena ANSI entre comillas 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'contiene todos los caracteres de control ASCII excepto \n(y NUL), por lo que puedes usarla en combinación con [:print:]para una emulación bastante sólida de [^\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'']

mklement0 avatar Jun 18 '2014 03:06 mklement0

Esto puede parecer un poco extraño, pero intenta:

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

Es decir, utilice una nueva línea real en lugar de \n.

¡La explicación es que tienes un extraño sed! Para obtener más información, consulte el manual de mac sed: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/sed.1.html

En la descripción del scomando allí, dice:

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.

Además, en la descripción de la -iopción, dice que la extensión no es opcional y que si no quieres una debes especificar un argumento vacío. ¡Así que al final todo tiene sentido!

ooga avatar Jun 18 '2014 00:06 ooga