¿Cómo insertar una nueva línea delante de un patrón?


138

¿Cómo insertar una nueva línea antes de un patrón dentro de una línea?

Por ejemplo, esto insertará una nueva línea detrás del patrón regex.

sed 's/regex/&\n/g'

¿Cómo puedo hacer lo mismo pero delante del patrón?

Dado este archivo de entrada de muestra, el patrón para coincidir es el número de teléfono.

some text (012)345-6789

Debe convertirse

some text
(012)345-6789


1
@NilsvonBarth, ¿por qué una pregunta directa es una mala pregunta?
Josh

Respuestas:


177

Esto funciona bashy zsh, probado en Linux y OS X:

sed 's/regexp/\'$'\n/g'

En general, para $seguido de un literal de cadena entre comillas simples, se bashrealiza una sustitución de barra diagonal inversa de estilo C, por ejemplo, $'\t'se traduce a una pestaña literal. Además, sed quiere que su literal de nueva línea se escape con una barra diagonal inversa, de ahí lo \anterior $. Y, por último, el signo de dólar en sí no debe citarse para que sea interpretado por el shell, por lo tanto, cerramos la cotización antes del $y luego la abrimos nuevamente.

Editar : como se sugiere en los comentarios de @ mklement0, esto también funciona:

sed $'s/regexp/\\\n/g'

Lo que sucede aquí es: todo el comando sed ahora es una cadena de estilo C, lo que significa que la barra invertida que sed requiere que se coloque antes de que la nueva línea literal se escape con otra barra invertida. Aunque es más legible, en este caso no podrá realizar sustituciones de cadenas de shell (sin volver a hacerlo feo).


77
Esto me da "nueva línea sin escape dentro del patrón de sustitución" en OSX.
Matt Gibson

@Matt Gibson es muy extraño porque la "nueva línea sin escape" se da solo cuando tienes una nueva línea real sin una barra diagonal inversa dentro del patrón de sustitución. Mi código anterior funciona, de hecho, en algunos otros shells también, por ejemplo, zsh, ksh.
mojuba

3
@ Matt Gibson ... o si olvida la barra invertida antes de '$' \ n en mi código.
mojuba

77
Tal como están escritas, estas expresiones reemplazan la expresión regular completamente con una nueva línea, en lugar de insertar una nueva línea en el medio de una línea existente como se solicitó. Así es como yo utilicé una forma modificada de esta respuesta para insertar un salto de línea entre dos patrones coincidentes: sed '\(first match\)\(second match\)/\1\'$'\n''\2/g'. Tenga en cuenta las dos comillas simples después de \ n. La primera cierra la $sección " " para que el resto de la línea no se vea afectada por ella. Sin esas citas, el \ 2 fue ignorado.
David Ravetti

12
Otra opción es usar una sola cadena entre comillas ANSI C : lo sed $'s/regexp/\\\n/g'que mejora la legibilidad; la única advertencia es que luego debe duplicar todos los \ caracteres literales .
mklement0

43

Algunas de las otras respuestas no funcionaron para mi versión de sed. Cambiando la posición de &y \nfuncionó.

sed 's/regexp/\n&/g' 

Editar: Esto no parece funcionar en OS X, a menos que lo instale gnu-sed.


9
No estoy seguro de que esto funcione en todas las versiones de sed. Intenté esto en mi Mac y el \ n solo aparece como 'n'
Todd Gamblin

3
Pasé 15 minutos en la Mac en mi trabajo, antes de leer tu respuesta. Go Apple!
Rick77

1
Para aquellos que usan homebrew: brew install gnu-sedseguido degsed 's/regexp/\n&/g'
aaaaaa

1
... seguido deecho 'alias sed=gsed' >> ~/.bashrc
Proximo

36

En sed, no puede agregar nuevas líneas en la secuencia de salida fácilmente. Necesita usar una línea de continuación, que es incómoda, pero funciona:

$ sed 's/regexp/\
&/'

Ejemplo:

$ echo foo | sed 's/.*/\
&/'

foo

Ver aquí para más detalles. Si quieres algo un poco menos incómodo, puedes intentar usarlo perl -pecon grupos de coincidencias en lugar de sed:

$ echo foo | perl -pe 's/(.*)/\n$1/'

foo

$1 se refiere al primer grupo coincidente en la expresión regular, donde los grupos están entre paréntesis.


¿Por qué dice que no puede agregar nuevas líneas? Puedes simplemente hacer sed 's / regexp / & \ n / g' Eso es todo
Andres

2
Esto es lo menos hacky que puedes hacer en una Mac para insertar nueva línea (\ n no funciona en una Mac)
Pylinux

La versión de Perl se puede modificar para realizar la edición en el lugarperl -pi -e 's/(.*)/\n$1/' foo
epónimo

2
@Andres: (Principalmente) las implementaciones de Sed solo con características POSIX, como la versión BSD que también viene con OS X, no admiten secuencias de escape de caracteres de control en la parte de sustitución de una sllamada de función (a diferencia de la implementación Sed de GNU , que sí lo hace) . La respuesta anterior funciona con ambas implementaciones; Para obtener una descripción general de todas las diferencias, consulte aquí .
mklement0

29

En mi Mac, lo siguiente inserta una 'n' en lugar de una nueva línea:

sed 's/regexp/\n&/g'

Esto reemplaza con nueva línea:

sed "s/regexp/\\`echo -e '\n\r'`/g"

Estaba haciendo edición en línea sed -i '' -e ...y estaba teniendo problemas con una ^Mletra M (ctrl + m) escrita en el archivo. Terminé usando perl con los mismos parámetros.
Steve Tauber,

2
¡Tenga en cuenta el hecho de que el segundo código inserta un código especial de nueva línea LF CR (reverso del MS-DOS CR LF)! Tanto los sistemas operativos tipo Unix como Mac OS X usan solo LF ( \n).
pabouk

Algo más en mi expresión sed estaba causando tanta infelicidad (a pesar de que funcionaba bien sin la echo...nueva línea) que acabo de hacer esto en vim.
Ahmed Fasih

1
O simplemente: sed "s/regexp/`echo`/g"- esto producirá un solo LF en lugar de LF-CR
mojuba

2
@mojuba: No: `echo`dará como resultado una cadena vacía , porque las sustituciones de comandos invariablemente recortan todas las nuevas líneas finales. No hay forma de usar una sustitución de comando para insertar directamente una nueva línea nueva (y la inserción \n\r, es decir, un CR adicional, es una idea terrible).
mklement0

15
echo one,two,three | sed 's/,/\
/g'

1
+1 funcionó perfectamente y bastante sencillo / fácil de recordar
gMale

2
Esta respuesta es en realidad una solución sed en lugar de una solución bash . Cualquier cosa que use construcciones similares $'\n'depende del shell para generar la nueva línea. Dichas soluciones pueden no ser portátiles. Este es. Por supuesto, también es un duplicado del segundo ejemplo en la respuesta de tgamblin de 2009.
ghoti

10

En este caso, no uso sed. Yo uso tr.

cat Somefile |tr ',' '\012' 

Esto toma la coma y la reemplaza con el retorno de carro.


1
Encontré que esto también funciona: cat Somefile | tr ',' '\n'YMMV
LS

9

Puede usar perl one-liners como lo hace con sed, con la ventaja de una expresión regular perl completa soporte (que es mucho más poderoso que lo que obtiene con sed). También hay muy poca variación en las plataformas * nix: perl es generalmente perl. Por lo tanto, puede dejar de preocuparse por cómo hacer que la versión particular de sed de su sistema haga lo que desea.

En este caso, puedes hacer

perl -pe 's/(regex)/\n$1/'

-pe pone a Perl en un bucle de "ejecutar e imprimir", muy parecido al modo de operación normal de sed.

' cita todo lo demás para que el caparazón no interfiera

()Alrededor de la expresión regular hay un operador de agrupación. $1en el lado derecho de la sustitución imprime lo que coincida dentro de estos parens.

Finalmente, \nes una nueva línea.

Independientemente de si está utilizando paréntesis como operador de agrupación, debe escapar de cualquier paréntesis que intente hacer coincidir. Entonces, una expresión regular para que coincida con el patrón que enumeras arriba sería algo así como

\(\d\d\d\)\d\d\d-\d\d\d\d

\(o \)coincide con un par literal, y \dcoincide con un dígito.

Mejor:

\(\d{3}\)\d{3}-\d{4}

Me imagino que puedes descubrir qué están haciendo los números entre llaves.

Además, puede usar delimitadores que no sean / para su expresión regular. Entonces, si necesitas unir / no tendrás que escapar. Cualquiera de los siguientes es equivalente a la expresión regular al comienzo de mi respuesta. En teoría, puede sustituir cualquier carácter por el estándar / 's.

perl -pe 's#(regex)#\n$1#'
perl -pe 's{(regex)}{\n$1}'

Un par de pensamientos finales.

usar en -nelugar de -peactuar de manera similar, pero no se imprime automáticamente al final. Puede ser útil si desea imprimir por su cuenta. Por ejemplo, aquí hay un grep-like ( m/foobar/es una coincidencia de expresiones regulares):

perl -ne 'if (m/foobar/) {print}'

Si le resulta problemático lidiar con las nuevas líneas y desea que se maneje mágicamente por usted, agregue -l. Sin embargo, no fue útil para el OP, que estaba trabajando con nuevas líneas.

Sugerencia adicional: si tiene instalado el paquete pcre, viene con él pcregrep, que utiliza expresiones regulares compatibles con perl.


4

Hmm, las líneas nuevas escapadas parecen funcionar en versiones más recientes de sed(Tengo GNU sed 4.2.1),

dev:~/pg/services/places> echo 'foobar' | sed -r 's/(bar)/\n\1/;'
foo
bar

1
Como se mencionó, esto funciona con varias versiones de GNU sed, pero no con el sed incluido con macOS.
LS

4
echo pattern | sed -E -e $'s/^(pattern)/\\\n\\1/'

funcionó bien en El Captitán con ()apoyo


Esto funcionó muy bien e incluso le da un comando completo para probar y extrapolar para especializarse para el propio propósito. ¡Buen trabajo!
jxramos

3

Para insertar una nueva línea para transmitir en Linux, utilicé:

sed -i "s/def/abc\\\ndef/" file1

Donde file1estaba:

def

Antes del reemplazo de sed in situ, y:

abc
def

Después del reemplazo de sed en el lugar. Tenga en cuenta el uso de \\\n. Si los patrones tienen un "interior, escapa usando \".


Para mí, el código anterior no funciona. sedinserta en \nlugar de LF porque ingresa \\nel parámetro desde el shell. --- Este código funciona: sed -i "s/def/abc\ndef/" file1. --- GNU sed version 4.2.1, GNU bash, version 4.1.2(1) / 4.2.25(1)(CentOS versión 6.4 / Ubuntu 12.04.3).
pabouk

2

en sed puede hacer referencia a grupos en su patrón con "\ 1", "\ 2", ... así que si el patrón que está buscando es "PATRÓN", y desea insertar "ANTES" delante de él , puedes usar, sin escapar

sed 's/(PATTERN)/BEFORE\1/g'

es decir

  sed 's/\(PATTERN\)/BEFORE\1/g'

Recién lo hice: contenido del archivo de prueba = "ABC ABC ABC". Ran "sed 's / \ (ABC \) / \ n \ 1 / g' testfile, obtuve las nuevas líneas. Experimente con los escapes, intente agregar 1 cosa a la vez a su patrón, por ejemplo, asegúrese de que coincida con el patrón, a continuación, comprobar la coincidencia de grupo, a continuación, añadir la comprobación de nueva línea.
Steve B.

Sólo traté exactamente eso y me "nabc nabc nabc'¿Está utilizando alguna otra versión de sed.?
Todd Gamblin

Es probable que escapar de la concha se interponga en el camino de los intentos de tgamblin. poner los argumentos sed completos en comillas simples como lo hizo Steve B debería solucionarlo. Sin embargo, es posible que las diferentes versiones de sed no entiendan el \ n para nueva línea.
Dan Pritts

2

También puede hacer esto con awk, usando -vpara proporcionar el patrón:

awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file

Esto verifica si una línea contiene un patrón dado. Si es así, agrega una nueva línea al comienzo.

Vea un ejemplo básico:

$ cat file
hello
this is some pattern and we are going ahead
bye!
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' file
hello
this is some 
pattern and we are going ahead
bye!

Tenga en cuenta que afectará a todos los patrones en una línea:

$ cat file
this pattern is some pattern and we are going ahead
$ awk -v patt="pattern" '$0 ~ patt {gsub(patt, "\n"patt)}1' d
this 
pattern is some 
pattern and we are going ahead

1
¿Qué hace el 1 en esto?
whatahitson

1
@whatahitson 1se usa en Awk como una abreviatura de {print $0}. La razón es que cualquier condición que se evalúe como Verdadero desencadena la acción predeterminada de Awk, que consiste en imprimir el registro actual.
Fedorqui 'SO deja de dañar'

1

Esto funciona en MAC para mí

sed -i.bak -e 's/regex/xregex/g' input.txt sed -i.bak -e 's/qregex/\'$'\nregex/g' input.txt

Dono si es perfecto ...


1

Después de leer todas las respuestas a esta pregunta, todavía me llevó muchos intentos obtener la sintaxis correcta para el siguiente script de ejemplo:

#!/bin/bash
# script: add_domain
# using fixed values instead of command line parameters $1, $2
# to show typical variable values in this example
ipaddr="127.0.0.1"
domain="example.com"
# no need to escape $ipaddr and $domain values if we use separate quotes.
sudo sed -i '$a \\n'"$ipaddr www.$domain $domain" /etc/hosts

El script agrega una nueva línea \nseguida de otra línea de texto al final de un archivo usando un solo sedcomando.


1
sed -e 's/regexp/\0\n/g'

\ 0 es nulo, por lo que su expresión se reemplaza por nulo (nada) y luego ...
\ n es la nueva línea

En algunos sabores de Unix no funciona, pero creo que es la solución a su problema.

echo "Hello" | sed -e 's/Hello/\0\ntmow/g'
Hello
tmow

0

En vi en Red Hat, pude insertar retornos de carro usando solo el carácter \ r. Creo que esto ejecuta internamente 'ex' en lugar de 'sed', pero es similar, y vi puede ser otra forma de hacer ediciones masivas, como parches de código. Por ejemplo. Estoy rodeando un término de búsqueda con una declaración if que insiste en retornos de carro después de las llaves:

:.,$s/\(my_function(.*)\)/if(!skip_option){\r\t\1\r\t}/

Tenga en cuenta que también tuve que insertar algunas pestañas para que las cosas se alineen mejor.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.