Come posso scorrere solo le directory in bash?


251

Ho una cartella con alcune directory e alcuni file (alcuni sono nascosti, a cominciare dal punto).

for d in *; do
 echo $d
done

passerà in rassegna tutti i file, ma voglio scorrere solo attraverso le directory. Come lo faccio?

Risposte:


334

È possibile specificare una barra alla fine per abbinare solo le directory:

for d in */ ; do
    echo "$d"
done

7
Si noti che include anche collegamenti simbolici alle directory.
Stéphane Chazelas,

1
quindi come escluderesti i collegamenti simbolici?
rubo77,

3
@ rubo77: puoi verificare [[ -L $d ]]se $ d è un collegamento simbolico.
Choroba,

1
@AsymLabs È errato, set -Pinfluisce solo sui comandi che cambiano directory. sprunge.us/TNac
Chris Down

4
@choroba: [[ -L "$f" ]]in questo caso non escluderemo i collegamenti simbolici con */, devi eliminare la barra finale [[ -L "${f%/}" ]](vedi Test per il collegamento con barra finale )
rubo77


30

Attenzione che la soluzione di choroba, sebbene elegante, può suscitare comportamenti imprevisti se non ci sono directory disponibili nella directory corrente. In questo stato, anziché saltare il forciclo, bash eseguirà il ciclo esattamente una volta dove dè uguale a */:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

Ti consiglio di utilizzare quanto segue per proteggere da questo caso:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

Questo codice eseguirà il ciclo attraverso tutti i file nella directory corrente, verificando se si ftratta di una directory, quindi echo fse la condizione ritorna vera. Se fè uguale a */, echo $fnon verrà eseguito.


3
Molto più facile shopt -s nullglob.
Choroba,

21

Se è necessario selezionare file più specifici di quelli utilizzati solo dalle directory finde passarli a while read:

shopt -s dotglob
find * -prune -type d | while IFS= read -r d; do 
    echo "$d"
done

Utilizzare shopt -u dotglobper escludere directory nascoste (o setopt dotglob/ unsetopt dotglobin zsh).

IFS=per evitare la divisione di nomi di file contenenti uno dei $IFS, ad esempio:'a b'

vedi AsymLabs risposta sotto per ulteriori findopzioni


modifica:
Nel caso in cui sia necessario creare un valore di uscita all'interno del ciclo while, è possibile aggirare la subshell aggiuntiva con questo trucco:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -prune -type d)

2
Ho trovato questa soluzione on: stackoverflow.com/a/8489394/1069083
rubo77

11

Puoi usare pure bash per questo, ma è meglio usare find:

find . -maxdepth 1 -type d -exec echo {} \;

(trova inoltre includerà directory nascoste)


8
Si noti che non include collegamenti simbolici alle directory. È possibile utilizzare shopt -s dotglobper bashincludere directory nascoste. Il tuo includerà anche .. Si noti inoltre che -maxdepthnon è un'opzione standard ( -pruneè).
Stéphane Chazelas,

2
L' dotglobopzione è interessante ma dotglobsi applica solo all'uso di *. find .includerà sempre le directory nascoste (e anche la directory corrente)
rubo77

6

Questo viene fatto per trovare sia le directory visibili che quelle nascoste nella presente directory di lavoro, esclusa la directory principale:

per scorrere semplicemente le directory:

 find -path './*' -prune -type d

per includere collegamenti simbolici nel risultato:

find -L -path './*' -prune -type d

fare qualcosa per ogni directory (esclusi i collegamenti simbolici):

find -path './*' -prune -type d -print0 | xargs -0 <cmds>

per escludere directory nascoste:

find -path './[^.]*' -prune -type d

per eseguire più comandi sui valori restituiti (un esempio molto inventato):

find -path './[^.]*' -prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

invece di 'sh -c' può anche usare 'bash -c', ecc.


Cosa succede se l'eco '* /' in 'per d in echo * /' contiene, diciamo, 60.000 directory?
AsymLabs

Verrà visualizzato l'errore "Troppi file" ma questo può essere risolto: Come aggirare "Troppi file aperti" in debian
rubo77

1
L'esempio di xargs funzionerebbe solo per un sottoinsieme di nomi di directory 9e.g. quelli con spazi o newline non funzionerebbero). Meglio usare ... -print0 | xargs -0 ...se non sai quali sono i nomi esatti.
Anthon,

1
@ rubo77 ha aggiunto un modo per escludere file / directory nascosti ed esecuzione di più comandi - ovviamente questo può essere fatto anche con uno script.
AsymLabs

1
Ho aggiunto un esempio nella mia risposta in fondo, come fare qualcosa in ogni directory usando una funzione confind * | while read file; do ...
rubo77

3

Puoi scorrere tutte le directory incluse le directory nascoste (che iniziano con un punto) in una riga e più comandi con:

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

nota: l' uso dell'elenco */ .*/funziona in bash, ma mostra anche le cartelle .e ..mentre in zsh non le mostrerà ma genererà un errore se non ci sono file nascosti nella cartella

Una versione più pulita che includerà directory nascoste ed escluderà ../ sarà con l'opzione dotglob:

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

(o setopt dotglobin zsh)

puoi deselezionare dotglob con

shopt -u dotglob

2

Ciò includerà il percorso completo in ciascuna directory nell'elenco:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done

1
si rompe quando si utilizzano cartelle con spazi
Alejandro Sazo,

0

Utilizzare findcon -execper scorrere ciclicamente le directory e chiamare una funzione nell'opzione exec:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -prune -type d -exec bash -c 'dosomething "$0"' {} \;

Utilizzare shopt -s dotglobo shopt -u dotglobper includere / escludere directory nascoste


0

Questo elenca tutte le directory insieme al numero di sottodirectory in un determinato percorso:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done

-1
ls -d */ | while read d
do
        echo $d
done

This is one directory with spaces in the name- ma viene analizzato come multiplo.
Piskvor,

-5
ls -l | grep ^d

o:

ll | grep ^d

Puoi impostarlo come alias


1
Sfortunatamente, non penso che questo risponda alla domanda, che era "Voglio passare in rassegna solo le directory" - che è leggermente diversa da questa lsrisposta basata su, che elenca le directory.
Jeff Schaller

1
Non è mai una buona idea analizzare l'output di ls: mywiki.wooledge.org/ParsingLs
codeforester
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.