Estoy aprendiendo sed. Todo parecía ir bien hasta que me encontré con la N (varias líneas a continuación). Creé este archivo (guide.txt) para fines de práctica / comprensión / contexto. Aquí está el contenido de dicho archivo ...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Por lo tanto, mi objetivo es sustituir TODAS las instancias de "Administrador de red" con "Usuario del sistema". Debido a que la primera instancia de "Administrador de red" está separada por una nueva línea (\ n), necesito el siguiente operador de varias líneas (N) para agregar la línea que comienza con "Administrador" con la línea anterior que termina con "Red \ n" . No hay problema. Pero también quiero capturar todas las demás instancias de una sola línea de "Administrador de red".
De mi investigación, aprendí que necesitaré dos comandos de sustitución; uno para la cadena separada de nueva línea y uno para los demás. Además, hay algunos cambios debido a la última línea que contiene la coincidencia de sustitución y la línea múltiple siguiente. Así que creo esto ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Esto devuelve estos resultados ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Pensé que la sustitución de una sola línea capturaría todas las instancias "normales" de "Administrador de red" y lo cambiaría por "Usuario del sistema", mientras que la instrucción de varias líneas funcionaría su magia en la instancia separada de nueva línea, pero como usted Puedo verlo devuelto, lo que considero, resultados inesperados.
Después de un poco de violín, aterricé en esto ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
Y voilà, obtengo la salida deseada de ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
¿Por qué funciona esto y el script sed original no? Realmente quiero entender esto.
Gracias de antemano por cualquier ayuda.
Respuestas:
Mientras aprende
sed
, me tomaré el tiempo para agregar a la respuesta de @ John1024:1) Tenga en cuenta que está utilizando
\n
la cadena de reemplazo. Esto funciona en GNUsed
, pero no es parte de POSIX, por lo que insertará una barra diagonal inversa y unan
en muchos otrossed
s (el uso\n
en el patrón es portátil, por cierto).En lugar de esto, sugiero que hagas
s/Network\([[:space:]]\)Administrator/System\1User/g
:[[:space:]]
coincidirá con la nueva línea o el espacio en blanco, por lo que no necesitas doss
comandos, sino combinarlos en uno. Al rodearlo con\(...\)
puede referirse a él en el reemplazo: El\1
será reemplazado por lo que coincida en el primer par de\(\)
.2) Para hacer coincidir correctamente los patrones en dos líneas, debe conocer el
N;P;D
patrón:El
N
siempre es añadir una línea al lado (a excepción de la última línea, es por eso que es "dirigido" con$!
(= si no es la última línea, siempre se debe tener en cuenta a precederN
a$!
evitar terminar el guión) accidentalmente Luego, después de la sustitución de los.P
Imprime sólo la primera línea en el espacio del patrón y luegoD
elimina esta línea y comienza el siguiente ciclo con los restos del espacio del patrón (sin leer la siguiente línea). Esto es probablemente lo que originalmente pretendía.Recuerde este patrón, a menudo lo necesitará.
3) Otro patrón útil para la edición multilínea, especialmente cuando hay más de dos líneas involucradas: Mantenga la recopilación de espacio, como le sugerí a John:
Lo repito para explicarlo:
H
agrega cada línea al espacio de espera. Como esto daría como resultado una nueva línea adicional antes de la primera línea, la primera línea debe moverse en lugar de agregarse1h
. Lo siguiente$!d
significa "para todas las líneas excepto la última, elimine el espacio del patrón y comience de nuevo". Por lo tanto, el resto del script solo se ejecuta para la última línea. En este punto, todo el archivo se recopila en el espacio de retención (¡así que no lo use para archivos muy grandes!) Y log
mueve al espacio del patrón, por lo que puede hacer todos los reemplazos a la vez como puede con la-z
opción de GNUsed
.Este es otro patrón útil que sugiero tener en cuenta.
fuente
Primero, tenga en cuenta que su solución realmente no funciona. Considere este archivo de prueba:
Y luego ejecuta el comando:
El problema es que el código no sustituye al último
Network\nAdministrator
.Esta solución funciona:
También podemos aplicar esto a su
guide.txt
:La clave es seguir leyendo en líneas hasta que encuentre una que no termine
Network
. Cuando eso se logra, las sustituciones se pueden hacer.Nota de compatibilidad: Todo el uso anterior
\n
en el texto de reemplazo. Esto requiere GNU sed. No funcionará en BSD / OSX sed.[Punta de sombrero para Philippos .]
Versión multilínea
Si ayuda a aclarar, aquí está el mismo comando dividido en varias líneas:
Cómo funciona
:a
Esto crea una etiqueta
a
./Network$/{ $!{N;ba} }
Si esta línea termina con
Network
, entonces, si esta no es la última línea ($!
), lea y agregue la siguiente línea (N
) y vuelva a la etiquetaa
(ba
).s/Network\nAdministrator/System\nUser/g
Realice la sustitución con la nueva línea intermedia.
s/Network Administrator/System User/g
Realice la sustitución con el espacio intermedio en blanco.
Solución más simple (solo GNU)
Con GNU sed ( no BSD / OSX), solo necesitamos un comando sustituto:
Y en el
guide.txt
archivo:En este caso,
-z
le dice a sed que lea hasta el primer carácter NUL. Como los archivos de texto nunca tienen un carácter nulo, esto tiene el efecto de leer todo el archivo a la vez. Luego podemos hacer la sustitución sin preocuparnos por perder una línea.Este método no es bueno si el archivo es enorme (generalmente significa gigabytes). Si es tan grande, entonces leerlo todo a la vez podría dañar la RAM del sistema.
Solución que funciona tanto en sed GNU como BSD
Como sugirió Phillipos , la siguiente es una solución portátil:
fuente
Network Administrator
se divide entre la primera y la segunda línea de ese par, su solución realiza la sustitución con éxito. Luego imprime esas dos líneas y lee en el siguiente par. Sin embargo, si la segunda línea del primer par termina conNetwork
y la primera línea del segundo par comienza conAdministrator
, el código lo pierde. Mi código lo evita leyendo en líneas hasta que encuentra uno que no termina conNetwork
.sed
: El\n
reemplazo no está definido en el estándar.sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'
Es una forma portátil de hacerlo.