¿Puede sed reemplazar nuevos caracteres de línea?

43

¿Hay algún problema con sed y el nuevo carácter de línea?
Tengo un archivo test.txt con los siguientes contenidos

aaaaa  
bbbbb  
ccccc  
ddddd  

Lo siguiente no funciona:
sed -r -i 's/\n/,/g' test.txt

Sé que puedo usar tresto, pero mi pregunta es por qué no parece posible con sed.

Si este es un efecto secundario del procesamiento del archivo línea por línea, me interesaría saber por qué sucede esto. Creo que grepelimina nuevas líneas. ¿Sed hace lo mismo?

Jim
fuente
1
En este caso, sed podría no ser la mejor herramienta para usar (por ejemplo, "tr"). Hay herramientas que son más intuitivas, más fáciles de leer / mantener, funcionan mejor (especialmente en grandes datos), etc. ... No use su martillo para colocar los tornillos (incluso si funciona). Puede encontrar una comparación en: http://slash4.de/blog/python/sed-replace-newline-or-python-awk-tr-perl-xargs.html
omoser
2
tragregaría un final ,y generaría una línea sin terminar. Lo mejor es usar pasteen su lugar:paste -sd , test.txt
Stéphane Chazelas

Respuestas:

49

Con GNU sedy proporcionado POSIXLY_CORRECTno está en el entorno (para entrada de una sola línea):

sed -i ':a;N;$!ba;s/\n/,/g' test.txt

Desde https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n :

  1. crear una etiqueta a través de :a
  2. agregue la línea actual y la siguiente al espacio del patrón a través de N
  3. si estamos antes de la última línea, bifurca a la etiqueta creada $!ba( $!significa no hacerlo en la última línea (ya que debería haber una nueva línea final)).
  4. finalmente, la sustitución reemplaza cada nueva línea con una coma en el espacio del patrón (que es el archivo completo).
Anthon
fuente
Esto parece indicar que el problema es que sed lee línea por línea. Pero no puedo entender por qué es esto un problema. Simplemente podría leer la línea y reemplazar el nuevo carácter de línea (o el último carácter) con un,
Jim
1
@ jim Parece que no está en el búfer para que coincida, pero no soy fluido con sed, tal vez alguien más pueda arrojar una luz sobre eso. Creo que debe ampliar su Q con esa información específica, para que las personas tengan más probabilidades de leerla y, con suerte, responder.
Anthon
Esto da como resultadoba: Event not found
krb686
@ krb686 ¿A qué se refiere "Esto"? ¿Ejecutó el sedcomando anterior con esas opciones exactas? En que test.txt archivo? ¿Con qué versión de sed(probar sed --version)?
Anthon
@Anthon Lo siento, creo que quise decir "el". Leí otra publicación SO que me informó que csh requiere que escape del !. Curiosamente, eso todavía no funcionó para mí y terminé teniendo que escapar dos veces !en mi .cshguión. Así que realmente no tengo un problema en este momento, pero ¿sabes por qué podría ser? Lo que funcionó para mí fuesed :a;N;$\\!ba;s/\n/ /g'
krb686
17

Esto funciona con GNU sed:

sed -z 's/\n/,/g' 

-z está incluido desde 4.2.2

NÓTESE BIEN. -zcambia el delimitador a caracteres nulos ( \0). Si su entrada no contiene caracteres nulos, toda la entrada se trata como una sola línea. Esto puede venir con sus limitaciones .

Para evitar que se reemplace la nueva línea de la última línea, puede volver a cambiarla:

sed -z 's/\n/,/g;s/,$/\n/'

(Que es la sedsintaxis de GNU nuevamente, pero no importa ya que todo es solo GNU)

Hielke Walinga
fuente
3
Esto también reemplazará la nueva línea final que podría no ser lo que OP quiere ... compare el resultado con la solución de mikeserv .
don_crissti
7

Desde el sitio web de Oracle:

La utilidad sed funciona leyendo secuencialmente un archivo, línea por línea, en la memoria. Luego realiza todas las acciones especificadas para la línea y vuelve a colocar la línea en la memoria para volcarla en la terminal con los cambios solicitados. Después de que todas las acciones se hayan llevado a cabo en esta línea, lee la siguiente línea del archivo y repite el proceso hasta que termine con el archivo.

Básicamente, esto significa que debido a que sed está leyendo línea por línea, el carácter de nueva línea no coincide.

La solución de https://stackoverflow.com/questions/1251999/sed-how-can-i-replace-a-newline-n es:

sed ':a;N;$!ba;s/\n/,/g'

o, en una versión portátil (sin ;concatenar después de las etiquetas de marca de salto)

sed -e ':a' -e 'N;$!ba' -e 's/\n/,/g'

En esa página se proporciona una explicación de cómo funciona.

usuario204992
fuente
Utilicé una forma modificada de esto para analizar los registros de VPN y poner al usuario "autenticado" y la información de la marca de tiempo en la misma línea. ¡Aclamaciones!
user208145
Tenga en cuenta que esa sintaxis es específica de GNU, e incluso con GNU sed, si POSIXLY_CORRECT está en el entorno y la entrada tiene solo una línea, no habrá salida.
Stéphane Chazelas
5

sedsiempre elimina el \newline final justo antes de llenar el espacio del patrón, y luego agrega uno antes de escribir los resultados de su script. Se \npuede tener un ewline en el espacio de patrones por varios medios, pero nunca si no es el resultado de una edición. Esto es importante: los \newlines en sedel espacio del patrón siempre reflejan un cambio y nunca ocurren en la secuencia de entrada. \nLas líneas electrónicas son el único delimitador con el que un sedder puede contar con una entrada desconocida.

Si desea reemplazar todas las \nlíneas electrónicas con comas y su archivo no es muy grande, puede hacer lo siguiente:

sed 'H;1h;$!d;x;y/\n/,/'

Eso agrega cada línea de entrada al hespacio anterior, excepto la primera, que en su lugar sobrescribe el hespacio anterior, siguiendo un \ncarácter de línea de flujo. Luego delige cada línea, no la $!última de la salida. En la última línea H, los espacios antiguos y de patrón se xcambian y todos los \ncaracteres de línea electrónica se y///traducen a comas.

Para archivos grandes, este tipo de cosas pueden causar problemas: sedel búfer en los límites de línea, que puede desbordarse fácilmente con acciones de este tipo.

mikeserv
fuente
2

Alternativamente, puede usar una sintaxis un poco más simple:

sed ':a;N;s/\n/,/g;ba'

... simplemente cambiando el orden de la secuencia.

Rodec
fuente
3
Pero ejecuta el scomando para cada línea de entrada en un espacio de patrón que es cada vez más grande.
Stéphane Chazelas
1

Hay algo de magia sed muy agradable aquí. Y algunos buenos puntos planteados sobre el desbordamiento del espacio del patrón. Me encanta usar sed incluso cuando no es la forma más simple, porque es muy compacto y potente. Sin embargo, tiene sus limitaciones, y para grandes cantidades de datos, el espacio del patrón debería ser mahoosive.

GNU dice esto:

Para aquellos que desean escribir scripts sed portátiles, tenga en cuenta que se sabe que algunas implementaciones limitan las longitudes de línea (para el patrón y los espacios de retención) a no más de 4000 bytes. El estándar posix especifica que las implementaciones sed conformes deben soportar al menos 8192 bytes de longitud de línea. GNU sed no tiene límite incorporado en la longitud de la línea; siempre que pueda malloc () más memoria (virtual), puede alimentar o construir líneas todo el tiempo que desee.
Sin embargo, la recursión se utiliza para manejar subpatrones y repeticiones indefinidas. Esto significa que el espacio de pila disponible puede limitar el tamaño del búfer que ciertos patrones pueden procesar.

No tengo mucho que agregar, pero me gustaría señalarle hacia mi guía de referencia para sed . Es excelente. http://www.grymoire.com/Unix/Sed.html

y aquí está mi solución:

for i in $(cat test.txt); do echo -n $i','; done; echo '' >> somewhere

bien funciona

xeuari
fuente
-1

Digamos que desea reemplazar las nuevas líneas por \n. Quería hacer eso, así que esto es lo que hice:

(echo foo; echo bar; echo baz) | sed -r '$!s/$/\\n/' | tr -d '\n' 
# Output: foo\nbar\nbaz

Esto es lo que hace: para todas las líneas, excepto la última , agregar \n. Luego, elimine las nuevas líneas con tr.

Camilo Martin
fuente
-rsolo está disponible en GNU sed, no en BSD.
kenorb