Come unire più righe di nomi di file in una con delimitatore personalizzato?


441

Vorrei unire il risultato ls -1in una riga e delimitarlo con quello che voglio.

Ci sono comandi Linux standard che posso usare per raggiungere questo obiettivo?

Risposte:


689

Simile alla prima opzione ma omette il delimitatore finale

ls -1 | paste -sd "," -

29
Proprio come una nota, la versione di paste che ho provato richiede un argomento "-" alla fine per dirgli di leggere da STDIN. ad esempio, ls -1 | paste -s -d ":" - non sono sicuro che sia universale con tutte le versioni di pasta
Andy White,

4
questo è meglio perché consente il delimitatore vuoto :)
Yura Purbeev,

2
Nota pasteottiene -(input standard) come predefinito, almeno sul mio paste (GNU coreutils) 8.22.
fedorqui "SO smettere di danneggiare" il

1
l'ho appena votato, questo è e ora ha gli stessi voti della risposta selezionata. QUESTA È LA RISPOSTA. nessun delimitatore finale
thebugfinder

1
Il delimitatore vuoto può essere specificato usando "\0", quindi ha paste -sd "\0" -funzionato per me!
Brad Parks,

379

EDIT : semplicemente " ls -m " Se vuoi che il delimitatore sia una virgola

Ah, il potere e la semplicità!

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

Cambia la virgola " , " come preferisci. Nota che questo include una "virgola finale"


46
+1, ma una versione più elaborata dovrebbe gestire last \ n in modo diverso
mouviciel

5
Se il nome del file contiene un \nal suo interno, questo sostituirà anche quello.
codaddict

3
@ShreevatsaR: intende non aggiungere un finale "," Credo. cosìls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
Chris

7
@Chris: sedpotresti essere un po 'più efficiente con il carattere di fine marcatore:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72

2
L'uso di sedafter trsembra solo rimuovere l'ultimo simbolo sembra irragionevole. Vado conls -1 | tr '\n' ',' | head -c -1
reddot

29

Questo sostituisce l'ultima virgola con una nuova riga:

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

ls -m include newline al carattere di larghezza dello schermo (80 ° per esempio).

Principalmente Bash (solo lsesterno):

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

Usando readarray(aka mapfile) in Bash 4:

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

Grazie a gniourf_gniourf per i suggerimenti.


Questo non si occuperà dei file con spazi bianchi nel nome. Prova questo: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && touch "questo è un file" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir

1
@dimir: molte delle risposte a questa domanda soffrono di questo problema. Ho modificato la mia risposta per consentire nomi di file con tabulazioni o spazi, ma non newline.
In pausa fino a nuovo avviso.

La tua versione bash soffre anche di espansioni di percorso. Per creare un array da linee, si prega di considerare l'utilizzomapfile (Bash ≥4) come: mapfile -t files < <(ls -1). Non c'è bisogno di giocherellare IFS. Ed è anche più corto.
gniourf_gniourf,

E quando si ha la matrice, è possibile utilizzare IFSper unire i campi: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Oppure usa un altro metodo se vuoi un separatore con più di un carattere.
gniourf_gniourf,

1
@gniourf_gniourf: ho incluso i tuoi suggerimenti nella mia risposta. Grazie.
In pausa fino a nuovo avviso.

24

Penso che questo sia fantastico

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

ORS è il "separatore del record di output", quindi ora le tue linee verranno unite con una virgola.


6
Ciò non esclude il delimitatore finale.
Derek Mahar,

6
Ciò è particolarmente fantastico grazie alla gestione dei separatori di record multi-carattere (ad es. " OR ")
Mat Schaffer,

15

La combinazione di impostazione IFSe uso di "$*"può fare quello che vuoi. Sto usando una subshell quindi non interferisco con $ IFS di questa shell

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

Per catturare l'output,

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

2
Hai qualche informazione in più su come setfunziona? A me sembra un po 'voodoo. Uno sguardo superficiale man setnon mi ha fornito molte informazioni.
Ehtesh Choudhury,

3
Se dai setun sacco di argomenti ma nessuna opzione, imposta i parametri posizionali ($ 1, $ 2, ...). --è lì per proteggere setnel caso in cui il primo argomento (o nome file in questo caso) inizi con un trattino. Vedi la descrizione --dell'opzione in help set. Trovo i parametri posizionali un modo conveniente per gestire un elenco di cose. Avrei potuto anche implementarlo con un array:output=$( files=(*); IFS=,; echo "${files[*]}" )
glenn jackman,

Questo è fantastico poiché non richiede l'esecuzione di alcun programma aggiuntivo e funziona con nomi di file che contengono spazi o persino newline.
Eric

1
@EhteshChoudhury Come type setvi dirà, set is a shell builtin. Quindi, man setnon aiuterà, ma help setlo farà. Risposta: "- Assegna tutti gli argomenti rimanenti ai parametri posizionali."
Stéphane Gourichon,

Dopo a set -- *. Ritardare l'espansione di *un livello che si può ottenere l'output corretto, senza la necessità di una conchiglia sub: IFS=',' eval echo '"$*"'. Naturalmente ciò cambierà i parametri posizionali.
Isaac,


9

Non reinventare la ruota.

ls -m

Lo fa esattamente.


L'OP voleva qualsiasi delimitatore, quindi sarebbe comunque necessario un tr per convertire le virgole. Aggiunge inoltre uno spazio dopo le virgole, ad esempio file1, file2, file3
rob

quindi usando ls -me trper rimuovere lo spazio dopo la virgola che farestils -m | tr -d ' '
Andy,

2
quell'uso di tr cancellerà gli spazi all'interno dei nomi dei file. meglio usaresed 's/, /,/g
glenn jackman

7

bash

mystring=$(printf "%s|" *)
echo ${mystring%|}

5
Un po 'più efficiente sarebbe usare "printf -v mystring"% s | "*" - che evita un fork per $ ()
camh

Ma in particolare non |trita il finale , @camh.
Christopher,

1
Bene, bashcoreutils giusti e gnuprintf
ThorSummoner,

@camh Ma printf -vfunzionerà solo in bash, mentre la risposta presentata funziona su molti tipi di shell.
Isaac,

@Christopher Sì, atto ad eliminare il trascinamento |, a condizione che entrambe le linee vengono utilizzati: printf -v mystring "%s|" * ; echo ${mystring%|}.
Isaac,

7

Questo comando è per i fan di PERL:

ls -1 | perl -l40pe0

Qui 40 è il codice ascii ottale per lo spazio.

-p elaborerà riga per riga e stamperà

-l si occuperà di sostituire il finale \ n con il carattere ascii che forniamo.

-e deve informare PERL che stiamo eseguendo la riga di comando.

0 significa che in realtà non esiste alcun comando da eseguire.

perl -e0 è uguale a perl -e ''


6

Per evitare la potenziale confusione newline per tr, potremmo aggiungere il flag -b a ls:

ls -1b | tr '\n' ';'


5

Aggiungendo in cima alla risposta di majkinetor, ecco il modo di rimuovere il delimitatore finale (dal momento che non posso ancora commentare sotto la sua risposta):

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

Basta rimuovere tutti i byte finali quanti conta il delimitatore.

Mi piace questo approccio perché posso usare delimitatori multi carattere + altri vantaggi di awk:

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

MODIFICARE

Come ha notato Peter, il conteggio di byte negativi non è supportato nella versione nativa di MacOS di head. Questo tuttavia può essere facilmente risolto.

Innanzitutto, installa coreutils. "Le GNU Core Utilities sono le utilità di base per la manipolazione di file, shell e testo del sistema operativo GNU."

brew install coreutils

I comandi forniti anche da MacOS sono installati con il prefisso "g". Per esempiogls .

Una volta fatto ciò, puoi usare gheadun numero di byte negativo, o meglio, creare un alias:

alias head="ghead"

Nota: i conteggi di byte negativi sono supportati solo su alcune versioni di head, quindi questo non funzionerà ad esempio su macos.
Peter,

Grazie per la segnalazione. Ho aggiunto una soluzione alternativa per MacOS.
Aleksander Stelmaczonek,

4

Il modo 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 :

Questo è lineare nella complessità, sostituendo una sola volta dopo che tutte le linee sono state aggiunte nello spazio modello di sed.

La risposta di AnandRajaseka e alcune altre risposte simili, come qui , sono O (n²), perché sed deve sostituire ogni volta che una nuova riga viene aggiunta nello spazio modello.

Per confrontare,

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

3

Se la tua versione di xargs supporta il flag -d, questo dovrebbe funzionare

ls  | xargs -d, -L 1 echo

-d è la bandiera del delimitatore

Se non hai -d, puoi provare quanto segue

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

I primi xargs ti consentono di specificare il delimitatore che è una virgola in questo esempio.


3
-dspecifica il delimitatore di input con xarg GNU, quindi non funzionerà. Il secondo esempio mostra lo stesso problema di altre soluzioni qui di un delimitatore vagante alla fine.
Thor,

3
sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]

Spiegazione:

-e- indica un comando da eseguire
:a- è un'etichetta
/$/N- definisce l'ambito della corrispondenza per la linea corrente e (N) ext
s/\n/\\n/;- sostituisce tutta l'EOL con\n
ta; - vai all'etichetta a se la corrispondenza ha esito positivo

Tratto dal mio blog .



2

ls produce un output di colonna quando è collegato a una pipe, quindi il file -1 è ridondante.

Ecco un'altra risposta perl che utilizza la joinfunzione integrata che non lascia un delimitatore finale:

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

L'oscuro -0777fa in modo che perl legga tutti gli input prima di eseguire il programma.

alternativa alternativa che non lascia un delimitatore finale

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

0

lsha l'opzione -mper delimitare l'output con ", "una virgola e uno spazio.

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

reindirizzare questo risultato per trrimuovere lo spazio o la virgola consentirà di reindirizzare il risultato trper sostituire il delimitatore.

nel mio esempio sostituisco il delimitatore ,con il delimitatore;

sostituisci ;con qualsiasi delimitatore di carattere che preferisci poiché tr considera solo il primo carattere nelle stringhe che passi come argomenti.


0

Puoi usare chomp per unire più righe in una sola riga:

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

inserire la condizione di interruzione di riga nell'istruzione if.Può essere un carattere speciale o qualsiasi delimitatore.


0

Versione Quick Perl con gestione della barra finale:

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

Spiegare:

  • perl -E: esegue Perl con i supporti delle funzioni (diciamo, ...)
  • dì: stampa con un corriere di ritorno
  • join ",", ARRAY_HERE: unisciti a un array con ","
  • map {chomp; $ _} FILI: rimuove da ogni riga il ritorno del corriere e restituisce il risultato
  • <>: stdin, ogni linea è un ROW, accoppiandosi con una mappa creerà un array di ogni ROW
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.