sustituir - ¿Cómo puedo reemplazar una nueva línea(/ n) usando sed?
sed shell (30)
Solución fácil de entender
Tuve este problema El truco fue que necesitaba la solución para trabajar en sed
y tr
BSD (Mac OS X) y de GNU (Linux y Cygwin ):
$ echo ''foo
bar
baz
foo2
bar2
baz2'' /
| tr ''/n'' ''/000'' /
| sed ''s:/x00/x00.*:/n:g'' /
| tr ''/000'' ''/n''
Salida:
foo
bar
baz
(tiene nueva línea final)
Funciona en Linux, OS X y BSD , incluso sin el soporte de UTF-8 o con un terminal de mierda.
Usa
tr
para intercambiar la nueva línea con otro personaje.NULL
(/000
o/x00
) es bueno porque no necesita el soporte UTF-8 y no es probable que se use.Utilice
sed
para que coincida con el valorNULL
Use
tr
para intercambiar nuevas líneas adicionales si las necesita
¿Cómo puedo reemplazar una nueva línea ( /n
) usando el comando sed?
He intentado sin éxito:
sed ''s#/n# #g'' file
sed ''s#^$# #g'' file
¿Cómo lo arreglo?
Reemplace las nuevas líneas con cualquier cadena, y reemplace la última nueva línea también
Las soluciones pure tr
solo pueden reemplazar con un solo carácter, y las soluciones pur sed
no reemplazan la última nueva línea de la entrada. La siguiente solución soluciona estos problemas y parece ser segura para los datos binarios (incluso con una configuración regional UTF-8):
printf ''1/n2/n3/n'' |
sed ''s/%/%p/g;s/@/%a/g'' | tr ''/n'' @ | sed ''s/@/<br>/g;s/%a/@/g;s/%p/%/g''
Resultado:
1<br>2<br>3<br>
Solución a prueba de balas. Seguro de datos binarios y compatible con POSIX, pero lento.
POSIX sed requiere entrada de acuerdo con el archivo de texto POSIX y las definiciones de línea POSIX , por lo que no se permiten NULL-bytes ni líneas demasiado largas y cada línea debe terminar con una nueva línea (incluida la última línea). Esto dificulta el uso de sed para procesar datos de entrada arbitrarios.
La siguiente solución evita sed y en su lugar convierte los bytes de entrada en códigos octales y luego nuevamente en bytes, pero intercepta el código octal 012 (nueva línea) y emite la cadena de reemplazo en lugar de él. Por lo que sé, la solución es compatible con POSIX, por lo que debería funcionar en una amplia variedad de plataformas.
od -A n -t o1 -v | tr '' /t'' ''/n/n'' | grep . |
while read x; do [ "0$x" -eq 012 ] && printf ''<br>/n'' || printf "//$x"; done
Documentación de referencia POSIX: sh , lenguaje de comandos de shell , od , tr , grep , read , [ , printf .
Tanto read
, [
como printf
están incorporados al menos en bash, pero probablemente no esté garantizado por POSIX, por lo que en algunas plataformas podría ser que cada byte de entrada inicie uno o más procesos nuevos, lo que ralentizará las cosas. Incluso en bash, esta solución solo alcanza los 50 kB / s, por lo que no es adecuada para archivos grandes.
Probado en Ubuntu (bash, dash y busybox), FreeBSD y OpenBSD.
¿Quién necesita sed
? Aquí está el camino de bash
:
cat test.txt | while read line; do echo -n "$line "; done
@OP, si desea reemplazar las nuevas líneas en un archivo, puede usar dos2unix (o unix2dox)
dos2unix yourfile yourfile
En Mac OS X (usando FreeBSD sed):
# replace each newline with a space
printf "a/nb/nc/nd/ne/nf" | sed -E -e :a -e ''$!N; s//n/ /g; ta''
printf "a/nb/nc/nd/ne/nf" | sed -E -e :a -e ''$!N; s//n/ /g'' -e ta
En algunas situaciones, quizás pueda cambiar RS
a alguna otra cadena o carácter. De esta manera, / n está disponible para sub / gsub:
$ gawk ''BEGIN {RS="dn" } {gsub("/n"," ") ;print $0 }'' file
El poder de los scripts de shell es que si no sabes cómo hacerlo de una manera, puedes hacerlo de otra manera. Y muchas veces tiene más cosas que tener en cuenta que hacer una solución compleja para un problema simple.
Con respecto a lo que gawk es lento ... y lee el archivo en la memoria, no lo sé, pero a mi parecer gawk parece funcionar con una línea en ese momento y es muy rápido (no tan rápido como algunos otros) , pero el tiempo para escribir y probar también cuenta).
Procesé MB e incluso GB de datos, y el único límite que encontré es el tamaño de la línea.
En respuesta a la solución "tr" anterior, en Windows (probablemente usando la versión Gnuwin32 de tr), la solución propuesta:
tr ''/n'' '' '' < input
no estaba funcionando para mí, o por error, o reemplazaría el / nw / '''' por alguna razón.
Usando otra característica de tr, la opción "borrar" -d funcionó aunque:
tr -d ''/n'' < input
o ''/ r / n'' en lugar de ''/ n''
Es sed que introduce las nuevas líneas después de la sustitución "normal". Primero, recorta el carácter de nueva línea, luego procesa de acuerdo con sus instrucciones y luego introduce una nueva línea.
Usando sed puede reemplazar "el final" de una línea (no la nueva línea de caracteres) después de ser recortada, con una cadena de su elección, para cada línea de entrada; pero, sed dará salida a diferentes líneas. Por ejemplo, supongamos que desea reemplazar el "final de línea" por "===" (más general que un reemplazo con un solo espacio):
PROMPT~$ cat <<EOF |sed ''s/$/===/g''
first line
second line
3rd line
EOF
first line===
second line===
3rd line===
PROMPT~$
Para reemplazar la nueva línea de caracteres con la cadena, puede, ineficientemente, usar tr , como se señaló anteriormente, para reemplazar las nuevas líneas con un "carácter especial" y luego usar sed para reemplazar esa característica con la cadena que desea .
Por ejemplo:
PROMPT~$ cat <<EOF | tr ''/n'' $''/x01''|sed -e ''s//x01/===/g''
first line
second line
3rd line
EOF
first line===second line===3rd line===PROMPT~$
La respuesta con la: una etiqueta ...
¿Cómo puedo reemplazar una nueva línea (/ n) usando sed?
... no funciona en freebsd 7.2 en la línea de comando:
( echo foo ; echo bar ) | sed '':a;N;$!ba;s//n/ /g'' sed: 1: ":a;N;$!ba;s//n/ /g": unused label ''a;N;$!ba;s//n/ /g'' foo bar
Pero lo hace si coloca el script sed en un archivo o usa -e para "compilar" el script sed ...
> (echo foo; echo bar) | sed -e :a -e N -e ''$!ba'' -e ''s//n/ /g'' foo bar
o ...
> cat > x.sed << eof
:a
N
$!ba
s//n/ /g
eof
> (echo foo; echo bar) | sed -f x.sed
foo bar
Tal vez el sed en OS X es similar.
La versión Perl funciona de la forma que esperabas.
perl -i -p -e ''s//n//'' file
Como se señaló en los comentarios, vale la pena señalar que esto se edita en su lugar. -i.bak
le dará una copia de seguridad del archivo original antes del reemplazo en caso de que su expresión regular no sea tan inteligente como pensaba.
No soy un experto, pero supongo que en primer lugar deberías añadir la siguiente línea en el espacio del patrón, utilizando " N
". De la sección "Espacio de patrones multilínea" en "Comandos avanzados de sed" del libro sed & awk (Dale Dougherty y Arnold Robbins; O''Reilly 1997; página 107 en la vista previa ):
El comando multilínea Siguiente (N) crea un espacio de patrón multilínea leyendo una nueva línea de entrada y agregándola al contenido del espacio de patrón. Los contenidos originales del espacio del patrón y la nueva línea de entrada están separados por una nueva línea. La secuencia de escape "/ n" puede hacer coincidir los caracteres de nueva línea incrustados en patrones. En un espacio de patrón multilínea, el metacarácter "^" coincide con el primer carácter del espacio de patrón, y no con el (los) carácter (es) que sigue a ninguna línea nueva incrustada. De manera similar, "$" solo coincide con la nueva línea final en el espacio del patrón, y no con ninguna nueva línea (s) incrustada. Una vez que se ejecuta el comando Siguiente, el control pasa a los comandos subsiguientes en el script.
Del man sed
:
[2addr] N
Agregue la siguiente línea de entrada al espacio del patrón, utilizando un carácter de nueva línea incrustado para separar el material adjunto del contenido original. Tenga en cuenta que el número de línea actual cambia.
He usado esto para buscar (múltiples) archivos de registro mal formateados, en los que la cadena de búsqueda se puede encontrar en la siguiente línea "huérfana".
Para reemplazar todas las líneas nuevas con espacios usando awk, sin leer todo el archivo en la memoria:
awk ''{printf "%s ", $0}'' inputfile
Si quieres una nueva línea final:
awk ''{printf "%s ", $0} END {printf "/n"}'' inputfile
Puedes usar un personaje que no sea espacio:
awk ''{printf "%s|", $0} END {printf "/n"}'' inputfile
Podría usar xargs
- reemplazará /n
con un espacio por defecto.
Sin embargo, tendría problemas si su entrada tiene algún caso de una unterminated quote
, por ejemplo, si los signos de cotización en una línea dada no coinciden.
Tres cosas.
tr
(ocat
, etc.) no es absolutamente necesario. (GNU)sed
y (GNU)awk
, cuando se combinan, pueden hacer el 99.9% de cualquier procesamiento de texto que necesite.stream! = línea basada.
ed
es un editor basado en líneas.sed
no es Consulte la sección de conferencias para obtener más información sobre la diferencia. La mayoría de la gente confunde quesed
se basa en la línea porque, por defecto, no es muy codicioso en su coincidencia de patrones para coincidencias SIMPLES; por ejemplo, cuando se realiza una búsqueda de patrones y se reemplaza por uno o dos caracteres, por defecto solo se reemplaza en la primera coincidencia que encuentre (a menos que el comando global especifique lo contrario). Ni siquiera habría un comando global si estuviera basado en líneas en lugar de STREAM, porque evaluaría solo líneas a la vez. Intente ejecutared
; Notarás la diferencia.ed
es bastante útil si desea iterar sobre líneas específicas (como en un bucle for), pero la mayoría de las veces solo querrásed
.Habiendo dicho eso,
sed -e ''{:q;N;s//n/ /g;t q}'' file
funciona bien en la versión 4.2.1 de GNU
sed
. El comando anterior reemplazará todas las líneas nuevas con espacios. Es feo y un poco engorroso escribir, pero funciona bien. Los{}
se pueden omitir, ya que solo se incluyen por motivos de cordura.
Una alternativa awk más corta:
awk 1 ORS='' ''
Explicación
Un programa awk está formado por reglas que consisten en bloques de código condicionales, es decir:
condition { code-block }
Si se omite el bloque de código, se usa el valor predeterminado: { print $0 }
. Por lo tanto, el 1
se interpreta como una condición verdadera y la print $0
se ejecuta para cada línea.
Cuando awk
lee la entrada, la divide en registros según el valor de RS
(Record Separator), que por defecto es una nueva línea, por lo tanto, awk
analizará la entrada por línea. La división también implica eliminar RS
del registro de entrada.
Ahora, cuando se imprime un registro, se agrega ORS
(Separador de registro de salida), el valor predeterminado es nuevamente una nueva línea. Entonces, al cambiar ORS
a un espacio, todas las nuevas líneas se cambian a espacios.
Una solución que me gusta particularmente es agregar todo el archivo en el espacio de retención y reemplazar todas las nuevas líneas al final del archivo:
$ (echo foo; echo bar) | sed -n ''H;${x;s//n//g;p;}''
foobar
Sin embargo, alguien me dijo que el espacio de retención puede ser finito en algunas implementaciones sed.
Usando Awk:
awk "BEGIN { o=/"/" } { o=o /" /" /$0 } END { print o; }"
Utilicé un enfoque híbrido para sortear el tema de la nueva línea utilizando tr para reemplazar las nuevas líneas con pestañas, y luego las pestañas con lo que quisiera. En este caso, "
"ya que estoy tratando de generar saltos de HTML.
echo -e "a/nb/nc/n" |tr ''/n'' ''/t'' | sed ''s//t/ <br> /g''`
Utilice esta solución con GNU sed
:
sed '':a;N;$!ba;s//n/ /g'' file
Esto leerá todo el archivo en un bucle, luego reemplaza las nuevas líneas con un espacio.
Explicación:
- Crear una etiqueta a través de
:a
. - Agregue la línea actual y la siguiente al espacio del patrón mediante
N
- Si estamos antes de la última línea, bifurque a la etiqueta creada
$!ba
($!
Significa no hacerlo en la última línea ya que debería haber una nueva línea final). - Finalmente, la sustitución reemplaza cada nueva línea con un espacio en el espacio del patrón (que es el archivo completo).
Aquí hay una sintaxis compatible multiplataforma que funciona con BSD y OS X''s sed
(según el comentario de @Benjie ):
sed -e '':a'' -e ''N'' -e ''$!ba'' -e ''s//n/ /g'' file
Como puede ver, usar sed
para este problema simple es problemático. Para una solución más simple y adecuada vea esta respuesta .
Utilizar tr
lugar?
tr ''/n'' '' '' < input_filename
o eliminar completamente los caracteres de nueva línea:
tr -d ''/n'' < input.txt > output.txt
o si tienes la versión GNU (con sus opciones largas)
tr --delete ''/n'' < input.txt > output.txt
gnu sed tiene una opción -z
para null registros separados (líneas). Usted puede simplemente llamar:
sed -z ''s//n/ /g''
Respuesta rápida:
sed '':a;N;$!ba;s//n/ /g'' file
- : a crear una etiqueta ''a''
- N agregue la siguiente línea al espacio del patrón
- PS si no es la última línea , ba rama (ir a) etiqueta ''a''
- s sustituye , / / n / regex por nueva línea , / / por un espacio , / g coincidencia global (tantas veces como sea posible)
sed recorrerá los pasos 1 a 3 hasta que llegue a la última línea, logrando que todas las líneas entren en el espacio del patrón donde sed sustituirá a todos los caracteres / n
Alternativas :
Todas las alternativas, a diferencia de sed , no necesitarán llegar a la última línea para comenzar el proceso
con bash , lento
while read line; do printf "%s" "$line "; done < file
con perl , velocidad similar a la sed
perl -p -e ''s//n/ /'' file
con tr , más rápido que sed , puede reemplazarse por un solo personaje
tr ''/n'' '' '' < file
con pegar , tr- como velocidad, puede reemplazar por un solo personaje
paste -s -d '' '' file
con awk , tr- como la velocidad
awk 1 ORS='' '' file
Otra alternativa como "echo $ (<archivo)" es lenta, funciona solo en archivos pequeños y necesita procesar todo el archivo para comenzar el proceso.
Larga respuesta de la pregunta frecuente 5.10 :
5.10. ¿Por qué no puedo hacer coincidir o eliminar una nueva línea usando / n escape?
¿secuencia? ¿Por qué no puedo hacer coincidir 2 o más líneas usando / n?
El / n nunca coincidirá con la nueva línea al final de la línea porque
la nueva línea siempre se elimina antes de que la línea se coloque en el
espacio del patrón. Para obtener 2 o más líneas en el espacio del patrón, use
el comando ''N'' o algo similar (como ''H; ...; g;'').
Sed funciona así: sed lee una línea a la vez, corta el
terminando la nueva línea, coloca lo que queda en el espacio de patrón donde
el script sed puede solucionarlo o cambiarlo, y cuando el espacio del patrón
se imprime, agrega una nueva línea a stdout (o a un archivo). Si el
el espacio del patrón se elimina total o parcialmente con ''d'' o ''D'', el
No se agrega nueva línea en tales casos. Así, guiones como
sed ''s//n//'' file # to delete newlines from each line
sed ''s//n/foo/n/'' file # to add a word to the end of each line
NUNCA funcionará, porque la nueva línea final se elimina antes
la línea se pone en el espacio del patrón. Para realizar las tareas anteriores,
use uno de estos scripts en su lugar:
tr -d ''/n'' < file # use tr to delete newlines
sed '':a;N;$!ba;s//n//g'' file # GNU sed to delete newlines
sed ''s/$/ foo/'' file # add "foo" to end of each line
Dado que las versiones de sed distintas de GNU sed tienen límites para el tamaño de
Aquí se prefiere el patrón buffer, la utilidad ''tr'' de Unix.
Si la última línea del archivo contiene una nueva línea, se agregará GNU sed
esa nueva línea a la salida, pero borra todas las demás, mientras que tr
Borrar todas las nuevas líneas.
Para hacer coincidir un bloque de dos o más líneas, hay 3 opciones básicas:
(1) use el comando ''N'' para agregar la línea Siguiente al espacio del patrón;
(2) use el comando ''H'' al menos dos veces para agregar la línea actual
al espacio de retención, y luego recuperar las líneas del espacio de retención
con x, g, o g; o (3) use rangos de direcciones (vea la sección 3.3, arriba)
para hacer coincidir las líneas entre dos direcciones especificadas.
Las opciones (1) y (2) pondrán un / n en el espacio del patrón, donde
se puede abordar como se desee (''s / ABC / nXYZ / alphabet / g''). Un ejemplo
de usar ''N'' para eliminar un bloque de líneas aparece en la sección 4.13
("¿Cómo elimino un bloque de líneas consecutivas específicas ?"). Esta
ejemplo puede ser modificado cambiando el comando de borrar a algo
otra cosa, como ''p'' (imprimir), ''i'' (insertar), ''c'' (cambiar), ''a'' (añadir),
o ''s'' (sustituto).
La opción (3) no pondrá un / n en el espacio del patrón, pero sí lo hace.
empareja un bloque de líneas consecutivas, por lo que puede ser que no
Incluso necesitas el / n para encontrar lo que estás buscando. Desde GNU sed
La versión 3.02.80 ahora soporta esta sintaxis:
sed ''/start/,+4d'' # to delete "start" plus the next 4 lines,
además del rango tradicional ''/ from here /, / to there / {...}''
direcciones, puede ser posible evitar el uso de / n por completo.
Para eliminar líneas vacías:
sed -n "s/^$//;t;p;"
Puedes usar este método también
sed ''x;G;1!h;s//n/ /g;$!d''
Explicación
x - which is used to exchange the data from both space (pattern and hold).
G - which is used to append the data from hold space to pattern space.
h - which is used to copy the pattern space to hold space.
1!h - During first line won''t copy pattern space to hold space due to /n is
available in pattern space.
$!d - Clear the pattern space every time before getting next line until the
last line.
Flujo:
cuando se obtiene la primera línea de la entrada, se realiza el intercambio, por lo que 1 va al espacio de retención y / n al espacio del patrón, luego agrega el espacio de retención al espacio del patrón y luego se realiza la sustitución y se elimina el espacio del patrón.
Durante el intercambio de la segunda línea, 2 van al espacio de retención y 1 al espacio del patrón, luego G
agregan el espacio de retención al espacio del patrón, luego h
copian el patrón y se realiza y elimina la sustitución. Esta operación continúa hasta que se alcanza eof, luego se imprime el resultado exacto.
Otro método de GNU sed
, casi el mismo que la respuesta de Zsolt Botykai , pero utiliza sed
el comando de uso menos frecuente y
( transliterar ), que guarda un byte de código (el final g
):
sed '':a;N;$!ba;y//n/ /''
Uno esperaría que y
fuera más rápido que s
, (quizás a tr
velocidades, 20 veces más rápido), pero en GNU sed v4.2.2 y
es aproximadamente un 4% más lento que s
.
Más versión BSD portátil sed
:
sed -e '':a'' -e ''N;$!ba'' -e ''y//n/ /''
Publiqué esta respuesta porque probé con la mayoría del sed
ejemplo de recomendación proporcionado anteriormente, que no funciona para mí en mi caja de Unix y me da un mensaje de error Label too long: {:q;N;s//n/ /g;tq}
. Finalmente hice mi requerimiento y por lo tanto lo compartí aquí, que funciona en todos los entornos Unix / Linux:
line=$(while read line; do echo -n "$line "; done < yoursourcefile.txt)
echo $line |sed ''s/ //g'' > sortedoutput.txt
La primera línea eliminará toda la nueva línea del archivo yoursourcefile.txt
y producirá una sola línea. Y el segundo sed
comando eliminará todos los espacios de él.
sed ''1h;1!H;$!d
x;s//n/ /g'' YourFile
Esto no funciona para archivos grandes (límite de búfer), pero es muy eficiente si hay suficiente memoria para contener el archivo. (Corrección H
-> 1h;1!H
después de la buena observación de @hilojack)
Otra versión que cambia de línea nueva mientras lee (más CPU, menos memoria)
sed '':loop
$! N
s//n/ /
t loop'' YourFile
tr ''/n'' '' ''
es el comando.
Simple y fácil de usar.