¿Cómo unir varias líneas de nombres de archivo en uno con delimitador personalizado?

441

Me gustaría unir el resultado ls -1en una línea y delimitarlo con lo que quiera.

¿Hay algún comando estándar de Linux que pueda usar para lograr esto?

JavaRocky
fuente

Respuestas:

689

Similar a la primera opción pero omite el delimitador final

ls -1 | paste -sd "," -
Artem
fuente
29
Solo como una nota, la versión de pegar que probé requiere un argumento "-" al final para decirle que lea desde STDIN. Por ejemplo, ls -1 | paste -s -d ":" - no estoy seguro si eso es universal con todas las versiones de pegar
Andy White
44
este es mejor porque permite delimitador vacío :)
Yura Purbeev
2
Note pasteobtiene -(entrada estándar) por defecto, al menos en mi paste (GNU coreutils) 8.22.
Fedorqui 'así que deja de dañar'
1
acabo de votarlo, esto es y ahora tiene los mismos votos que la respuesta seleccionada. ESTA ES LA RESPUESTA. no se arrastra delimeter
thebugfinder
1
El delimitador vacío se puede especificar usando "\0", ¡así que paste -sd "\0" -funcionó para mí!
Brad Parks
378

EDITAR : Simplemente " ls -m " Si desea que su delimitador sea una coma

¡Ah, el poder y la simplicidad!

ls -1 | tr '\n' ','

Cambie la coma " , " a lo que quiera. Tenga en cuenta que esto incluye una "coma final"

zaf
fuente
46
+1, pero una versión más elaborada debería manejar la última \ n de manera diferente
mouviciel
55
Si el nombre del archivo contiene un \n, también lo reemplazará.
codaddict
3
@ShreevatsaR: quiere decir que no agregue un "," creo que sigue. asíls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
Chris
77
@ Chris: sedpodría ser un poco más eficiente con el carácter de marcador final:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72
2
Usar sedafter trparece simplemente eliminar el último símbolo no parece razonable. Voy conls -1 | tr '\n' ',' | head -c -1
reddot
29

Esto reemplaza la última coma con una nueva línea:

ls -1 | tr '\n' ',' | sed 's/,$/\n/'

ls -m incluye nuevas líneas en el carácter de ancho de pantalla (80 por ejemplo).

Principalmente Bash (solo lses externo):

saveIFS=$IFS; IFS=$'\n'
files=($(ls -1))
IFS=,
list=${files[*]}
IFS=$saveIFS

Usando readarray(aka mapfile) en Bash 4:

readarray -t files < <(ls -1)
saveIFS=$IFS
IFS=,
list=${files[*]}
IFS=$saveIFS

Gracias a gniourf_gniourf por las sugerencias.

Pausado hasta nuevo aviso.
fuente
Esto no se ocupará de los archivos con espacios en blanco en el nombre. Pruebe este: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && toque "this is a file" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir
1
@dimir: Muchas de las respuestas a esta pregunta sufren de este problema. He editado mi respuesta para permitir nombres de archivo con pestañas o espacios, pero no nuevas líneas.
Pausado hasta nuevo aviso.
Su versión bash también sufre expansiones de nombre de ruta. Para construir una matriz de líneas, por favor, considere el uso de mapfile(Bash ≥4) como: mapfile -t files < <(ls -1). No es necesario jugar con ellos IFS. Y también es más corto.
gniourf_gniourf
Y cuando se tiene la matriz, se puede utilizar IFSpara unirse a los campos: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. O use otro método si desea un separador con más de un carácter.
gniourf_gniourf
1
@gniourf_gniourf: He incluido sus sugerencias en mi respuesta. Gracias.
Pausado hasta nuevo aviso.
24

Creo que este es genial

ls -1 | awk 'ORS=","'

ORS es el "separador de registro de salida" por lo que ahora sus líneas se unirán con una coma.

majkinetor
fuente
66
Esto no excluye el delimitador final.
Derek Mahar
66
Esto es especialmente impresionante debido al manejo de separadores de registros de varios caracteres (por ejemplo, " OR ")
Mat Schaffer
15

La combinación de configuración IFSy uso de "$*"puede hacer lo que quieras. Estoy usando un subshell para no interferir con el $ IFS de este shell

(set -- *; IFS=,; echo "$*")

Para capturar la salida,

output=$(set -- *; IFS=,; echo "$*")
Glenn Jackman
fuente
2
¿Tienes más información sobre cómo setfunciona? A mí me parece un poco vudú. una mirada superficial man settampoco me proporcionó mucha información.
Ehtesh Choudhury
3
Si da setun montón de argumentos pero no tiene opciones, establece los parámetros posicionales ($ 1, $ 2, ...). --está allí para proteger seten caso de que el primer argumento (o nombre de archivo en este caso) comience con un guión. Vea la descripción de la --opción en help set. Considero que los parámetros posicionales son una forma conveniente de manejar una lista de cosas. También podría haber implementado esto con una matriz:output=$( files=(*); IFS=,; echo "${files[*]}" )
Glenn Jackman
Esto es excelente ya que no requiere la ejecución de ningún programa adicional y funciona con nombres de archivos que contienen espacios o incluso líneas nuevas.
Eric
1
@EhteshChoudhury Como type setle dirá, set is a shell builtin. Por lo tanto, man setno ayudará, pero help setlo hará. Respuesta: "- Asigne cualquier argumento restante a los parámetros posicionales".
Stéphane Gourichon
Después de a set -- *. El retraso de la expansión de *un nivel que puede obtener la salida correcta sin la necesidad de una concha substitución: IFS=',' eval echo '"$*"'. Por supuesto, eso cambiará los parámetros posicionales.
Isaac
9

No reinventes la rueda.

ls -m

Hace exactamente eso.

Tulains Córdova
fuente
El OP quería cualquier delimitador, por lo que aún necesitaría un tr para convertir las comas. También agrega un espacio después de las comas, es decir, archivo1, archivo2, archivo3
robe el
así que usando ls -my trpara eliminar el espacio después de la coma que haríasls -m | tr -d ' '
Andy
2
ese uso de tr eliminará espacios dentro de los nombres de archivo. mejor usosed 's/, /,/g
Glenn Jackman
7

solo golpe

mystring=$(printf "%s|" *)
echo ${mystring%|}
ghostdog74
fuente
55
Un poco más eficiente sería usar "printf -v mystring"% s | "*", que evita una bifurcación para $ ()
camh
Pero notablemente no |muerde el rastro, @camh.
Christopher
1
Bueno, solo bashy gnu coreutilsprintf
ThorSummoner
@camh Pero printf -vsolo funcionará en bash, mientras que la respuesta presentada funciona en muchos tipos de shell.
Isaac
@Christopher Sí, capaz de eliminar el arrastre |, siempre que se utilicen ambas líneas: printf -v mystring "%s|" * ; echo ${mystring%|}.
Isaac
7

Este comando es para los fanáticos de PERL:

ls -1 | perl -l40pe0

Aquí 40 es el código ascii octal para el espacio.

-p procesará línea por línea e imprimirá

-l se encargará de reemplazar el \ n final con el carácter ascii que proporcionamos.

-e informa a PERL que estamos realizando la ejecución de la línea de comandos.

0 significa que en realidad no hay ningún comando para ejecutar.

perl -e0 es lo mismo que perl -e ''

Sidharth C. Nadhan
fuente
6

Para evitar una posible confusión de nueva línea para tr, podríamos agregar el indicador -b a ls:

ls -1b | tr '\n' ';'
yabt
fuente
5

Además de la respuesta de majkinetor, esta es la forma de eliminar el delimitador final (ya que todavía no puedo comentar debajo de su respuesta):

ls -1 | awk 'ORS=","' | head -c -1

Simplemente elimine tantos bytes finales como su delimitador cuenta.

Me gusta este enfoque porque puedo usar delimitadores de caracteres múltiples + otros beneficios de awk:

ls -1 | awk 'ORS=", "' | head -c -2

EDITAR

Como Peter ha notado, el recuento de bytes negativo no es compatible con la versión nativa de cabeza de MacOS. Sin embargo, esto se puede solucionar fácilmente.

Primero, instale coreutils. "Las GNU Core Utilities son las utilidades básicas de manipulación de archivos, shell y texto del sistema operativo GNU".

brew install coreutils

Los comandos también proporcionados por MacOS se instalan con el prefijo "g". Por ejemplo gls.

Una vez que haya hecho esto, puede usar el gheadque tiene un conteo de bytes negativo, o mejor, hacer un alias:

alias head="ghead"
Aleksander Stelmaczonek
fuente
Nota: los recuentos de bytes negativos solo son compatibles con ciertas versiones de head, por lo que esto no funcionará, por ejemplo, en macos.
Peter
Gracias por señalar eso. He agregado una solución para MacOS.
Aleksander Stelmaczonek
4

El camino sed,

sed -e ':a; N; $!ba; s/\n/,/g'
  # :a         # label called 'a'
  # N          # append next line into Pattern Space (see info sed)
  # $!ba       # if it's the last line ($) do not (!) jump to (b) label :a (a) - break loop
  # s/\n/,/g   # any substitution you want

Nota :

Esto es lineal en complejidad, sustituyéndose solo una vez después de que todas las líneas se agreguen al espacio de patrón de sed.

La respuesta de @ AnandRajaseka , y algunas otras respuestas similares, como aquí , son O (n²), porque sed tiene que sustituir cada vez que se agrega una nueva línea al Espacio del Patrón.

Comparar,

seq 1 100000 | sed ':a; N; $!ba; s/\n/,/g' | head -c 80
  # linear, in less than 0.1s
seq 1 100000 | sed ':a; /$/N; s/\n/,/; ta' | head -c 80
  # quadratic, hung
zhazha
fuente
3

Si su versión de xargs admite el indicador -d, entonces esto debería funcionar

ls  | xargs -d, -L 1 echo

-d es la bandera delimitador

Si no tiene -d, puede intentar lo siguiente

ls | xargs -I {} echo {}, | xargs echo

El primer xargs le permite especificar su delimitador, que es una coma en este ejemplo.

Chris J
fuente
3
-despecifica el delimitador de entrada con GNU xargs, por lo que no funcionará. El segundo ejemplo exhibe el mismo problema que otras soluciones aquí de un delimitador parásito al final.
Thor
3
sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]

Explicación:

-e- denota un comando que se ejecutará
:a- es una etiqueta
/$/N- define el alcance de la coincidencia para la línea actual y la (N) ext
s/\n/\\n/;- reemplaza todos los EOL con \n
ta;- goto label a si la coincidencia es exitosa

Tomado de mi blog .

Anand Rajasekar
fuente
2

Puedes usar:

ls -1 | perl -pe 's/\n$/some_delimiter/'
codictorio
fuente
Esto no excluye el delimitador final.
Derek Mahar
2

lsproduce una salida de columna cuando se conecta a una tubería, por lo que -1es redundante.

Aquí hay otra respuesta perl usando la joinfunción incorporada que no deja un delimitador final:

ls | perl -F'\n' -0777 -anE 'say join ",", @F'

El oscuro -0777hace que Perl lea toda la entrada antes de ejecutar el programa.

alternativa de sed que no deja un delimitador final

ls | sed '$!s/$/,/' | tr -d '\n'
Thor
fuente
0

lstiene la opción -mde delimitar la salida con ", "una coma y un espacio.

ls -m | tr -d ' ' | tr ',' ';'

canalizar este resultado para treliminar el espacio o la coma le permitirá canalizar el resultado nuevamente trpara reemplazar el delimitador.

en mi ejemplo, reemplazo el delimitador ,con el delimitador;

reemplace ;con cualquier delimitador de caracteres que prefiera, ya que tr solo representa el primer carácter en las cadenas que pasa como argumentos.

Andy
fuente
0

Puede usar chomp para fusionar varias líneas en una sola línea:

perl -e 'while (<>) {if (/ \ $ /) {chomp; } print;} 'bad0> prueba

ponga la condición de salto de línea en la declaración if. Puede ser un carácter especial o cualquier delimitador.

Suman
fuente
0

Versión Quick Perl con manejo de barra final:

ls -1 | perl -E 'say join ", ", map {chomp; $_} <>'

Para explicar:

  • perl -E: ejecuta Perl con funciones compatibles (por ejemplo, ...)
  • decir: imprimir con una devolución del transportista
  • unirse ",", ARRAY_HERE: unir una matriz con ","
  • mapa {chomp; $ _} FILAS: elimine de cada línea el transportista que devuelve y devuelve el resultado
  • <>: stdin, cada línea es una FILA, uniéndose a un mapa creará una matriz de cada FILA
Celogeek San
fuente