2011-12-15 4 views
59

¿Cómo puedo unir varias líneas en una línea, con un separador donde estaban los caracteres de la nueva línea, y evitar un separador final y, opcionalmente, ignorar las líneas vacías?Conciso y portátil "join" en la línea de comandos de Unix

Ejemplo. Considere un archivo de texto, foo.txt, con tres líneas:

foo 
bar 
baz 

La salida deseada es:

foo,bar,baz 

El comando que estoy utilizando ahora:

tr '\n' ',' <foo.txt |sed 's/,$//g' 

Lo ideal sería algo como este:

cat foo.txt |join , 

Cuál es:

  1. la manera más portátil, sucinta, legible.
  2. la forma más concisa usando herramientas Unix no estándar.

Por supuesto que podría escribir algo, o simplemente usar un alias. Pero estoy interesado en conocer las opciones.

+0

posible duplicado de [Unir varias líneas en una con bash] (http://stackoverflow.com/questions/2764051/joining-multiple-lines-into-one-with-bash) –

Respuesta

93

Tal vez un poco sorprendente, paste es una buena manera de hacer esto:

paste -s -d"," 

esto no va a hacer frente a las líneas vacías que usted ha mencionado. Por eso, tubo de su texto a través grep, en primer lugar:

grep -v '^$' | paste -s -d"," - 
+0

@codaddict Ni yo, pero yo debo admitir que no me parece intuitivo en absoluto; siempre necesito consultar las páginas man para esto. Definitivamente tengo curiosidad por ver lo que otros sugieren. –

+0

¡Oye, gracias, ni siquiera sabía este comando! – fge

+0

Hay otras formas, pero ninguna más agradable (y las más divertidas son un poco bashy). – Sorpigal

4

Sólo por diversión, aquí es una solución todo-órdenes internas

IFS=$'\n' read -r -d '' -a data < foo.txt ; (IFS=, ; echo "${data[*]}" ;) 

Puede utilizar printf en lugar de echo si el salto de línea final es un problema.

Esto funciona mediante el establecimiento de IFS, los delimitadores que read se dividirán en adelante, a solo salto de línea y no otros espacios en blanco, a continuación, contando read a no dejar de leer hasta que alcanza una nul, en lugar de la nueva línea por lo general utiliza y añadir cada elemento leído en la matriz de datos (-a). Luego, en un subnivel a fin de no darle una paliza a la IFS del shell interactivo, nos propusimos IFS a , y ampliar la matriz con *, que delimita cada elemento de la matriz con el primer carácter de IFS

+0

interesante, sin embargo, la portabilidad no es excelente, ya que no existe la opción '-d' en el comando' leer 'puro' sh' shell. – mykhal

+0

@mykhal: Es cierto. Sin embargo, 'bash' se puede encontrar en muchos sistemas, por lo que tiene cierta utilidad. Si desea que las matrices de portabilidad estén probablemente fuera, de lo contrario, simplemente podría usar un ciclo 'while' para evitar la falta de' -d'. Para una versión adecuada y portátil de todos los builtins querrás algo como 'c =; mientras que IFS = read -r d; Hacer si ! [-z "$ d"]; luego printf "$ c $ d"; fi c =,; done Sorpigal

12

Este sed uno línea debe trabajar -

sed -e :a -e 'N;s/\n/,/;ba' file

prueba:

[jaypal:~/Temp] cat file 
foo 
bar 
baz 

[jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file 
foo,bar,baz 

Para manejar líneas vacías, puede eliminar las líneas vacías y canalizarlas al trazador de líneas anterior.

sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba' 
+6

¡excelente respuesta! ahora para investigar qué significan esos nuevos comandos sed. – fuzzyTew

+0

¡Una explicación sería agradable! –

+0

Es más claro combinar dos -e expresión en una, 'sed-e ': a; NORTE; s/\ n /, /; ba''. Pero este sigue siendo un método O (n²), porque sed hará alguna sustitución cada vez que se agregue una nueva línea. 'sed-e ': a; NORTE; $! ba; s/\ n /,/g'' es lineal, sustituyéndolo solo una vez después de que todas las líneas se anexen al Espacio de Patrón de sed. '$! ba' significa" si es la última línea ($) no (!) salte a (b) etiqueta: a (a), rompa el círculo " – zhazha

0

que necesitaba para llevar a cabo algo similar, la impresión de una lista separada por comas de los campos de un archivo, y estaba contento con la salida estándar de tuberías a xargs y ruby, así:

cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs ruby -e "puts ARGV.join(', ')" 
5

Perl:

cat data.txt | perl -pe 'if(!eof){chomp;$_.=","}' 

o aún más corto y más rápido, de manera sorprendente:

cat data.txt | perl -pe 'if(!eof){s/\n/,/}' 

o, si se quiere:

cat data.txt | perl -pe 's/\n/,/ unless eof' 
+1

Lo bueno de esto es que puede usar cualquier cadena en lugar de solo una coma simple. La respuesta aceptada es menos versátil. Me gusta especialmente la iteración final, aunque lo habría escrito como: 'perl -pe 's/\ n /,/a menos que eof' data.txt' (sin necesidad del gato espurio). –

0

Una manera sencilla de unirse a las líneas con el espacio en el lugar utilizando ex (también haciendo caso omiso de las líneas en blanco), utilice:

ex +%j -cwq foo.txt 

Si desea imprimir los resultados a la salida estándar, prueba:

ex +%j +%p -scq! foo.txt 

Para unir líneas sin espacios, utilizar +%j! en lugar de +%j.

utilizar diferentes delimitador, que es un poco más complejo:

ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt 

donde g/^$/d (o v/\S/d) elimina las líneas en blanco y s/\n/_/ es sustitución que funciona básicamente lo mismo que usar sed, sino para todas las líneas (%) Cuando finalice el análisis, imprima el búfer (%p). Y finalmente -cq! ejecutando el comando vi q!, que básicamente se cierra sin guardar (-s es silenciar la salida).

Tenga en cuenta que ex es equivalente a vi -e.

Este método es bastante portátil ya que la mayoría de los Linux/Unix se envían por defecto con ex/vi. Y es más compatible que con sed donde el parámetro en contexto (-i) no es la extensión estándar y la utilidad en sí misma es más orientada a la transmisión, por lo tanto, no es tan portátil.

7

¿Qué tal usar xargs?

para su caso

$ cat foo.txt | sed 's/$/, /' | xargs 

tener cuidado con el límite de longitud de entrada del comando xargs. (Esto significa que el archivo de entrada muy largo no puede ser manejado por esto.)

+0

Encontré el indicador '-L' en xargs útil' -L 50', 50 elementos por línea. – jmunsch

0

Tenía un archivo de registro donde algunos datos se dividieron en varias líneas. Cuando esto ocurrió, el último carácter de la primera línea fue el punto y coma (;). Me uní a estas líneas utilizando los siguientes comandos:

for LINE in 'cat $FILE | tr -s " " "|"' 
do 
    if [ $(echo $LINE | egrep ";$") ] 
    then 
     echo "$LINE\c" | tr -s "|" " " >> $MYFILE 
    else 
     echo "$LINE" | tr -s "|" " " >> $MYFILE 
    fi 
done 

El resultado es un archivo donde las líneas que se dividen en el archivo de registro eran una línea en mi nuevo archivo.

-1

Mi respuesta es:

awk '{printf "%s", ","$0}' foo.txt 

printf es suficiente. No necesitamos -F"\n" para cambiar el separador de campo.

+1

Esto agrega una coma espuria al comienzo de la salida. -1 por no probar. –

Cuestiones relacionadas