Come ottenere l'elenco dei file in una directory in uno script di shell?


159

Sto cercando di ottenere il contenuto di una directory usando lo script della shell.

La mia sceneggiatura è:

for entry in `ls $search_dir`; do
    echo $entry
done

dove $search_dirè un percorso relativo. Tuttavia, $search_dircontiene molti file con spazi bianchi nei loro nomi. In tal caso, questo script non viene eseguito come previsto.

So che potrei usare for entry in *, ma funzionerebbe solo per la mia directory corrente.

So che posso passare a quella directory, usare for entry in *quindi tornare indietro, ma la mia situazione particolare mi impedisce di farlo.

Ho due percorsi relativi $search_dire $work_dir, e devo lavorare su entrambi contemporaneamente, leggendoli creando / eliminando i file in essi ecc.

Quindi cosa faccio adesso?

PS: io uso bash.

Risposte:


274
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
Puoi spiegare perché for entry in "$search_dir/*"non funziona? Perché dobbiamo inserire /*al di fuori delle virgolette?
mrgloom,

6
@mrgloom: Perché devi lasciare alla shell glob il carattere jolly.
Ignacio Vazquez-Abrams,

non findsarebbe più veloce?
Alexej Magura,

4
La soluzione fornisce il percorso completo. E se volessi solo elencare ciò che era nella directory corrente?
ThatsRight Jack,

1
@mrgloom se vuoi farlo puoi farlo confor entry in "${search_dir}/*"
funilrys

28

Le altre risposte qui sono fantastiche e rispondono alla tua domanda, ma questo è il miglior risultato di Google per "bash get list of files in directory", (che stavo cercando per salvare un elenco di file) quindi ho pensato di pubblicare un rispondere a quel problema:

ls $search_path > filename.txt

Se desideri solo un determinato tipo (ad es. Qualsiasi file .txt):

ls $search_path | grep *.txt > filename.txt

Nota che $ search_path è facoltativo; ls> nomefile.txt eseguirà la directory corrente.


Non è necessario utilizzare grep per ottenere solo file .txt: `ls $ search_path / *. Txt> nomefile.txt '. Ma soprattutto, non si dovrebbe usare l'output del comando ls per analizzare i nomi dei file.
Victor Zamanian,

1
@VictorZamanian, puoi spiegare perché non dovremmo usare l'output di lsper analizzare i nomi dei file? Non ne ho mai sentito parlare prima.
samurai_jane,

1
@samurai_jane Ci sono molti link da fornire su questo argomento, ma ecco un primo risultato di ricerca: mywiki.wooledge.org/ParsingLs . Ho anche visto una domanda qui su SO sostenendo che le ragioni per non analizzare l'output di ls erano BS ed è stato molto elaborativo al riguardo. Ma le risposte / risposte sostenevano ancora che era una cattiva idea. Dai
Victor Zamanian

26

Questo è un modo per farlo dove la sintassi è più semplice per me capire:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./è la directory di lavoro corrente ma potrebbe essere sostituita con qualsiasi percorso
*.txtrestituisce nulla.txt
Puoi controllare ciò che verrà elencato facilmente digitando ills comando direttamente nel terminale.

Fondamentalmente, si crea una variabile che yourfilenamescontiene tutto ciò che il comando list restituisce come elemento separato e quindi lo si scorre ciclicamente. Il ciclo crea una variabile temporanea eachfileche contiene un singolo elemento della variabile in cui scorre, in questo caso un nome file. Questo non è necessariamente migliore delle altre risposte, ma lo trovo intuitivo perché ho già familiarità con il lscomando e la sintassi del ciclo for.


Funziona bene con uno script veloce o informale o con una riga, ma si interromperà se un nome file contiene nuove righe, a differenza delle soluzioni basate su glob.
Soren Bjornstad,

@SorenBjornstad grazie per il consiglio! Non sapevo che le nuove righe fossero consentite nei nomi dei file: che tipo di file potrebbe averli? Tipo, è qualcosa che si verifica comunemente?
rrr

Le nuove righe nei nomi dei file sono malvagie per questo motivo e per quanto ne so non c'è motivo legittimo per usarle. Non ne ho mai visto uno in natura. Detto questo, è del tutto possibile costruire maliziosamente nomi di file con newline in modo da sfruttarlo. (Ad esempio, immagina una directory contenente file A, Be C. Crea file chiamati B\nCe D, quindi scegli di eliminarli. Il software che non gestisce questo diritto potrebbe finire per eliminare i file preesistenti B e C invece anche se non avessi permesso di farlo.)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
So che questo è piuttosto vecchio ma non riesco a ottenere l'ultimo xargs -0 -i echo "{}"comando, mi dispiace spiegarmi un po '? In particolare cosa fa la -i echo "{}"parte? Inoltre ho letto dalla manpagina che -iora è deprecata e dovremmo usare -Iinsted.
drkg4b,

1
-isostituisce {}con l'arg.
Noel Yap,

Grazie! Questo è utile, anche per i più lenti come me penso che {}sia la stringa che viene sostituita con le corrispondenze dal findcomando.
drkg4b,

3
perchè usi xargs? per impostazione predefinita, findstampa ciò che trova ... è possibile eliminare tutto da -print0.
gniourf_gniourf,

1
Ciò non gestirà bene le voci dei file con spazi.
Noel Yap,

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

variazioni

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Appunti:

  • . : cartella corrente
  • rimuovi -maxdepth 1per cercare ricorsivamente
  • -type f: trova i file, non le directory ( d)
  • -not -path '*/\.*' : non tornare .hidden_files
  • sed 's/^\.\///g': rimuove l'elemento anteposto ./dall'elenco dei risultati

0

Ecco un altro modo di elencare i file all'interno di una directory (usando uno strumento diverso, non efficiente come alcune delle altre risposte).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Emette tutti i file della directory corrente. Il forciclo scorre su ogni nome di file e stampa su stdout.

Inoltre, se cerchi directory all'interno della directory, inseriscile nel forloop:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d controlla se il file è una directory.


0

La risposta accettata non restituirà il prefisso dei file con a. Per fare questo uso

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

Nella versione Linux con cui lavoro (x86_64 GNU / Linux) funziona come segue:

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1: questo è identico alla risposta accettata, tranne per il fatto che non cita le variabili. Non quotare $search_dirsignifica che si interrompe se c'è uno spazio nel nome della directory. (In questo caso puoi evitare di non citare $entry, ma sarebbe meglio fare una citazione.)
Soren Bjornstad,

Tui hai torto. La mia soluzione non è la stessa, anche se simile alla risposta accettata. E NON è sbagliato, funziona. Ecco cosa ho provato su diversi sistemi unix, incluso Mac OS, e ha funzionato su ognuno di essi. Il file con spazi vuoti, anche quando ha spazi vuoti iniziali nel nome, viene visualizzato correttamente. search_dir =. per l'ingresso in $ search_dir / *; fare eco $ entry; done ./ aa aaa.txt ./add ./apm_tables.txt Puoi votare le soluzioni quando puoi provare che la soluzione NON FUNZIONA o produce risultati errati.
Andrushenko Alexander

Se leggi il mio commento un po 'più da vicino, vedrai che il problema riguarda gli spazi nel nome della directory , non il nome del file . È diverso dal problema riscontrato dall'interrogatore, ma significa comunque che il codice può rompersi involontariamente! Ecco un esempio di ciò che non funziona: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Il ciclo viene eseguito due volte, una volta la stampa xe la seconda volta y/*, che presumibilmente non è il risultato previsto.
Soren Bjornstad,

E considero le variabili impropriamente non quotate pericolosamente sbagliate, il che è motivo di un voto negativo. Considerare questo codice per eliminare tutte le sottodirectory della directory di ricerca, lasciando i file: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Se c'è anche una xdirectory nella directory corrente, viene sommariamente eliminata, solo perché non hai citato la tua variabile!
Soren Bjornstad,

Bene, si prende sempre un codice creato per un'attività, si modifica leggermente l'attività e il codice diventa errato. Per la domanda posta la mia soluzione proposta funziona. Ma ... essendo uno sviluppatore, sono d'accordo con le tue argomentazioni: quando possiamo rendere un codice più solido e generale, dovremmo farlo. Quindi aggiungerò i qoutes. Questo renderà la mia risposta identica alla risposta data sopra, ma per motivi di discussione (che può essere utile per qualcuno), lascio anche la risposta.
Andrushenko Alexander,

-1

Basta inserire questo semplice comando:

ls -d */

Potresti fornire qualche spiegazione @Michael Sisko
Vipul

ls (list) -d (elenca solo le directory) * / (di qualsiasi modello, la barra rovesciata limita l'elenco solo alle directory).
Michael Sisko, il
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.