Come eseguire il loop over delle directory in Linux?


168

Sto scrivendo uno script in bash su Linux e ho bisogno di passare attraverso tutti i nomi delle sottodirectory in una determinata directory. Come posso scorrere queste directory (e saltare i file regolari)?

Ad esempio:
la directory data è /tmp/
che ha le seguenti sottodirectory:/tmp/A, /tmp/B, /tmp/C

Voglio recuperare A, B, C.


Risposte:


129
cd /tmp
find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n'

Una breve spiegazione:

  • find trova i file (ovviamente)

  • .è la directory corrente, che dopo cdè /tmp(IMHO è più flessibile che avere /tmpdirettamente nel findcomando. Hai solo un posto, il cd, da cambiare, se vuoi che vengano eseguite più azioni in questa cartella)

  • -maxdepth 1e -mindepth 1assicurati che findappaia solo nella directory corrente e non si includa .nel risultato

  • -type d cerca solo le directory

  • -printf '%f\n stampa solo il nome della cartella trovata (più una nuova riga) per ogni hit.

Et voilà!


A proposito: nota che anche tutte le risposte troveranno cartelle nascoste (ovvero cartelle che iniziano con un punto)! Se non lo desideri, aggiungi `-regex '\ ./ [^ \.]. *' 'Prima delle opzioni printf o exec.
Boldewyn,

1
Utile - se devi eseguire un solo comando per ogni colpo ;-) - Solo io ho diversi comandi da eseguire :-(.
Martin

Nessun problema: adatta questa soluzione: transnum.blogspot.de/2008/11/… All'interno del while..doneloop puoi impazzire.
Boldewyn,

1
Questo è un metodo molto carino per alcuni casi d'uso; findL' -execopzione ti consente di eseguire qualsiasi comando per ogni file / directory.
jtpereyda,

451

Tutte le risposte finora utilizzate find, quindi eccone una con solo la shell. Non sono necessari strumenti esterni nel tuo caso:

for dir in /tmp/*/     # list directories in the form "/tmp/dirname/"
do
    dir=${dir%*/}      # remove the trailing "/"
    echo ${dir##*/}    # print everything after the final "/"
done

18
+1 - Perché preoccuparsi di findquando puoi aggiungere una barra a un jolly
Tobias Kienzler

19
così for dir in */; do echo $dir; doneè per le directory nella directory corrente.
Ehtesh Choudhury,

41
Sarebbe bello se potesse contenere una spiegazione su cosa dir=${dir%*/}e cosa echo ${dir##*/}sta facendo.
Jeremy S.

11
È perché la variabile dir includerà la barra rovesciata alla fine. Lo sta semplicemente rimuovendo per avere un nome pulito. % rimuoverà / alla fine. ## rimuoverà / all'inizio. Questi sono operatori per gestire sottostringhe.
sfratini il

8
Se non c'è corrispondenza, il loop verrà attivato con /tmp/*/, sarebbe saggio includere un controllo per vedere se la directory esiste davvero.
J42,

41

Puoi scorrere tutte le directory incluse le directory nascoste (che iniziano con un punto) con:

for file in */ .*/ ; do echo "$file is a directory"; done

nota: l' utilizzo dell'elenco */ .*/funziona in zsh solo se esiste almeno una directory nascosta nella cartella. In bash mostrerà anche .e..


Un'altra possibilità per bash di includere directory nascoste sarebbe usare:

shopt -s dotglob;
for file in */ ; do echo "$file is a directory"; done

Se si desidera escludere i collegamenti simbolici:

for file in */ ; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

Per generare solo il nome della directory finale (A, B, C in questione) in ciascuna soluzione, utilizzalo nei loop:

file="${file%/}"     # strip trailing slash
file="${file##*/}"   # strip path and leading slash
echo "$file is the directoryname without slashes"

Esempio (funziona anche con directory che contengono spazi):

mkdir /tmp/A /tmp/B /tmp/C "/tmp/ dir with spaces"
for file in /tmp/*/ ; do file="${file%/}"; echo "${file##*/}"; done

16

Funziona con directory che contengono spazi

Ispirato da Sorpigal

while IFS= read -d $'\0' -r file ; do 
    echo $file; ls $file ; 
done < <(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d -print0)

Post originale (non funziona con gli spazi)

Ispirato da Boldewyn : esempio di loop con findcomando.

for D in $(find /path/to/dir/ -mindepth 1 -maxdepth 1 -type d) ; do
    echo $D ;
done

9
find . -mindepth 1 -maxdepth 1 -type d -printf "%P\n"

Anche questo è bello ed elimina la necessità di basename. Preferirei questo alla mia risposta.
Boldewyn,

6

La tecnica che uso più spesso è find | xargs. Ad esempio, se si desidera rendere leggibili in tutto il mondo tutti i file in questa directory e tutte le sue sottodirectory, è possibile:

find . -type f -print0 | xargs -0 chmod go+r
find . -type d -print0 | xargs -0 chmod go+rx

L' -print0opzione termina con un carattere NULL anziché uno spazio. L' -0opzione divide i suoi input allo stesso modo. Quindi questa è la combinazione da usare su file con spazi.

Puoi immaginare questa catena di comandi come prendere ogni output di riga finde incollarlo alla fine di un chmodcomando.

Se il comando che vuoi eseguire come argomento nel mezzo anziché alla fine, devi essere un po 'creativo. Ad esempio, dovevo cambiare in ogni sottodirectory ed eseguire il comando latemk -c. Quindi ho usato (da Wikipedia ):

find . -type d -depth 1 -print0 | \
    xargs -0 sh -c 'for dir; do pushd "$dir" && latexmk -c && popd; done' fnord

Questo ha l'effetto di for dir $(subdirs); do stuff; done, ma è sicuro per le directory con spazi nei loro nomi. Inoltre, le chiamate separate a stuffvengono effettuate nella stessa shell, motivo per cui nel mio comando dobbiamo tornare alla directory corrente con popd.


3

un loop bash minimo che puoi creare (basato sulla risposta ghostdog74)

for dir in directory/*                                                     
do                                                                                 
  echo ${dir}                                                                  
done

comprimere un sacco di file per directory

for dir in directory/*
do
  zip -r ${dir##*/} ${dir}
done                                               

Questo elenca tutti i file di directory, non solo le sottodirectory.
Dexteritas,

2

find . -type d -maxdepth 1


2
questa risposta è buona, tranne per il fatto che ricevo i percorsi completi, mentre desidero solo i nomi delle directory.
Erik Sapir,

2

Se si desidera eseguire più comandi in un ciclo for, è possibile salvare il risultato di findcon mapfile(bash> = 4) come variabile e passare attraverso l'array con ${dirlist[@]}. Funziona anche con directory contenenti spazi.

Il findcomando si basa sulla risposta di Boldewyn. Ulteriori informazioni sul findcomando sono disponibili qui.

IFS=""
mapfile -t dirlist < <( find . -maxdepth 1 -mindepth 1 -type d -printf '%f\n' )
for dir in ${dirlist[@]}; do
    echo ">${dir}<"
    # more commands can go here ...
done
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.