sed en el inserto OSX en una línea determinada

24

Así que he estado usando 'sed' en Linux por un tiempo, pero he tenido algunas dificultades para tratar de usarlo en OSX ya que 'POSIX sed' y 'GNU sed' tienen tantas pequeñas diferencias. Actualmente estoy luchando con cómo insertar una línea de texto después de un cierto número de línea. (en este caso, línea 4)

En Linux haría algo como esto:

sed --in-place "4 a\  mode '0755'" file.txt

Entonces en OSX probé esto:

sed -i "" "4 a\ mode '0755'" file.txt

Sin embargo, esto sigue dándome un error de 'caracteres adicionales después de \ al final de un comando'. Alguna idea de lo que está mal aquí? ¿Tengo un error tipográfico? ¿O no entiendo otra diferencia entre las versiones de sed?

CRThaze
fuente
2
Puede confirmar que esto no funciona en MacOS 10.6.8. Funciona bien en Debian 6.0.3.
tink

Respuestas:

24

Estrictamente hablando, la especificación POSIX parased requiere una nueva línea después de a\:

[1addr]a\
text

Escribir texto en la salida estándar como se describió anteriormente.

Esto hace que escribir una sola línea de un poco de dolor, que es probablemente la razón de la siguiente extensión de GNU a la a, iy ccomandos:

Como una extensión de GNU, si entre la anueva línea y la nueva hay otra que no sea una \secuencia de espacios en blanco , entonces el texto de esta línea, que comienza en el primer carácter que no sea un espacio en blanco después de a, se toma como la primera línea del bloque de texto . (Esto permite una simplificación en la creación de secuencias de comandos de un complemento de una línea). Esta extensión también funciona con los comandos iy c.

Por lo tanto, para ser portátil con su sedsintaxis, deberá incluir una nueva línea después de a\alguna manera. La forma más fácil es simplemente insertar una nueva línea citada:

$ sed -e 'a\
> text'

(donde $y >son indicaciones de shell). Si encuentra que es un dolor, bash[1] tiene la $' 'sintaxis de comillas para insertar escapes de estilo C, así que solo use

sed -e 'a\'$'\n''text'

[1] y mksh (r39b +) y algunos depósitos de bourne que no son bash (por ejemplo, / bin / sh en FreeBSD 9+)

jw013
fuente
Respuesta muy informativa. Gracias por la pista sobre la sintaxis de citas bash.
cjackson el
14

Como se describe en esta respuesta , es útil cuando se utilizan comandos sed como iy apara usar múltiples -e "..."cláusulas. Se entenderá que cada una de estas cláusulas está separada por líneas nuevas. De lo contrario, los comandos iy ason difíciles de usar en scripts sed en línea (están diseñados para usarse en un archivo de scripts sed multilínea invocado mediante sed -f file ...). Parece que no puede usar la nueva línea implícita introducida al final de una -ecláusula para separar a\la línea de texto y la que se agregará. Pero puede usarlo para terminar la línea de texto que se agregará.

En este caso específico, lo que está tratando de hacer podría lograrse con una sola -e ...cláusula. Solo tienes que usar el acomando correctamente. Según el estándar POSIX, adebe ir seguido de a \, luego debe seguir una nueva línea, luego se insertará el resto de la siguiente línea (hasta que se encuentre una nueva línea o el final de la -ecláusula). Entonces podrías hacer:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
fuente
2
Si desea ordenar las características en las que ha estado confiando son Gnu-isms, esta página puede ayudar: wiki.alpinelinux.org/wiki/Regex . Sin embargo, se limita solo a las funciones de expresiones regulares; no los otros comandos sed.
dubiousjim
5

GNU sed parece ser mucho más comúnmente usado que POSIX sed, basado en ejemplos / tutoriales que he usado de todos modos.

Descubrí que es mucho más fácil instalar GNU sed en cualquier máquina OSX que use. Si está interesado, la mejor manera de hacerlo es instalarlo a través de Homebrew .

Puede simplemente $ brew install gnu-sed, o obtener todas las utilidades comunes de GNU $ brew install coreutils.

Luego, cuando se encuentre con un problema de sintaxis con datealgún otro programa, simplemente puede usar la versión GNU. Finalmente, decidí que era más fácil usar siempre las versiones de GNU y ponerlas antes que las versiones del sistema en mi PATH.

Decano
fuente
5

Perl no sufre de tales idiosincrasias dependientes de GNU frente a no GNU versus "propietarias". Podrías hacerlo:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

El -n' option creates a loop that reads every line of the input file. Unlike its cousin-p (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -eseñala el comienzo del guión.

El $.denota el número de línea, comenzando con la línea 1. Por lo tanto, la línea de comando lee 'archivo' y cuando el número de línea es igual a 4, imprime esa línea seguida de lo que desee inyectar.

Me apresuraría a agregar que Perl debe sus raíces a sedAWK y C, por lo que la sintaxis para sustituciones simples, etc. no es una curva de aprendizaje muy empinada.

JRFerguson
fuente
¡Buena respuesta! Pero puedes simplificarlo un poco. Prueba esto: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. El -pinterruptor funciona bien aquí (ya que queremos imprimir cada línea); solo tenemos que cambiar la lógica de "imprimir después de la línea 4" a "imprimir antes de la línea 5". Y el -linterruptor hace que "\ n" se imprima automáticamente con cada print, para que no tenga que imprimirlo usted mismo.
JL