shell - que - Conciso y portátil "unirse" en la línea de comandos de Unix
shell unix comandos (9)
¿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 posterior y, opcionalmente, ignorar las líneas vacías?
Ejemplo. Considere un archivo de texto, foo.txt
, con tres líneas:
foo
bar
baz
El resultado deseado es:
foo,bar,baz
El comando que estoy usando ahora:
tr ''/n'' '','' <foo.txt |sed ''s/,$//g''
Idealmente sería algo como esto:
cat foo.txt |join ,
Que es:
- la forma más portátil, concisa y legible.
- 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.
¿Qué tal usar xargs?
para su caso
$ cat foo.txt | sed ''s/$/, /'' | xargs
Tenga cuidado con la longitud límite de entrada del comando xargs. (Esto significa que el archivo de entrada muy largo no puede ser manejado por esto).
Esta sed
una línea debería funcionar -
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 las 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''
Forma simple de unir las líneas con espacio in situ utilizando ex
(también ignorando las líneas en blanco), use:
ex +%j -cwq foo.txt
Si desea imprimir los resultados a la salida estándar, intente:
ex +%j +%p -scq! foo.txt
Para unir líneas sin espacios, use +%j!
en lugar de +%j
.
Para usar delimitador diferente, es un poco más complicado:
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 la sustitución que básicamente funciona igual que el uso de sed
, pero para todas las líneas ( %
). Cuando finalice el análisis, imprima el búfer ( %p
). Y finalmente -cq!
ejecutando vi q!
comando, 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 vienen con ex
/ vi
por defecto. Y es más compatible que con sed
donde el parámetro en -i
( -i
) no es la extensión estándar y la utilidad en sí misma está más orientada a la transmisión, por lo tanto, no es tan portátil.
Mi respuesta es:
awk ''{printf "%s", ","$0}'' foo.txt
printf
es suficiente. No necesitamos -F"/n"
para cambiar el separador de campo.
Necesitaba lograr algo similar, imprimir una lista de campos separados por comas de un archivo, y estaba contento con conectar STDOUT a xargs
y ruby
, así:
cat data.txt | cut -f 16 -d '' '' | grep -o "/d/+" | xargs ruby -e "puts ARGV.join('', '')"
Perl:
cat data.txt | perl -pe ''if(!eof){chomp;$_.=","}''
o aún más corto y más rápido, sorprendentemente:
cat data.txt | perl -pe ''if(!eof){s//n/,/}''
o, si quieres:
cat data.txt | perl -pe ''s//n/,/ unless eof''
Solo por diversión, aquí hay una solución completamente integrada
IFS=$''/n'' read -r -d '''' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )
Puede usar printf
lugar de echo
si la nueva línea final es un problema.
Esto funciona estableciendo IFS
, los delimitadores que read
se dividirán en solo línea nueva y no en otros espacios en blanco, luego le read
a la read
que deje de leer hasta que llegue a una nul
, en lugar de la línea nueva que usualmente usa, y agregará cada elemento leído a la matriz ( -a
) de datos. Luego, en una subshell para no dañar el IFS
del shell interactivo, establecemos IFS
en y ampliamos el conjunto con *
, que delimita cada elemento en el conjunto con el primer carácter en IFS
Tal vez un poco sorprendente, paste
es una buena manera de hacer esto:
paste -s -d","
Esto no tratará con las líneas vacías que mencionaste. Para eso, canaliza tu texto a través de grep
, primero:
grep -v ''^$'' | paste -s -d"," -
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 usando 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 dividieron en el archivo de registro fueron una línea en mi nuevo archivo.