2009-10-15 14 views
13

Esiste un modo semplice per elencare solo le directory in una determinata directory in Linux? Per spiegare meglio, posso fare:Elenca tutte le sottodirectory foglia in linux

find mydir -type d 

che dà:

mydir/src 
mydir/src/main 
mydir/bin 
mydir/bin/classes 

Quello che voglio invece è:

mydir/src/main 
mydir/bin/classes 

posso fare questo in uno script bash che loop oltre le linee e rimuove la riga precedente se la riga successiva contiene il percorso, ma mi chiedo se esiste un metodo più semplice che non usa i loop di bash.

+0

Con il vostro esempio, 'trovare mydir -mindepth 2 - il tipo d' funzionerebbe, ma non lo farà quando si hanno più profondità massime. Vuoi davvero elencare solo le directory che non contengono altre directory o vuoi vedere un particolare livello di struttura delle directory? – Cascabel

+0

Sì, grazie per il chiarimento - il mio esempio è stato un esempio semplicistico. Stavo davvero cercando una soluzione più generale. Inoltre, sto cercando di vedere la struttura generale delle directory, non mi interessa davvero dei file nelle directory (quindi anche leaf significa "leaf directory" in questo contesto). Grazie. – amol

risposta

10
find . -type d | sort | awk '$0 !~ last "/" {print last} {last=$0} END {print last}' 
+0

Grazie, solo il tipo di soluzione che stavo cercando :) – amol

+0

Questo produrrà risultati corretti, ma non ordinati, senza il 'sort' (quindi potresti metterlo alla fine, invece, se vuoi). –

+0

Questo non funziona se si hanno due directory: foo/bar e foo/bar_baz. foo/bar non verrà stampato. – mattismyname

4

Non riesco a pensare a nulla che lo faccia senza un ciclo. Così, qui sono alcuni loop:

Questo visualizza le directory foglia sotto la directory corrente, a prescindere dalla loro profondità:

for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "$dir" ; prev="$dir"; done 

Questa versione gestisce correttamente i nomi delle directory spazi che contengono:

saveIFS=$IFS; IFS=$'\n'; for dir in $(find -depth -type d); do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done; IFS=$saveIFS 

Qui è una versione che utilizza il suggerimento Jefromi:

find -depth -type d | while read dir; do [[ ! $prev =~ $dir ]] && echo "${dir}" ; prev="$dir"; done 
+0

L'OP chiedeva se esistesse un modo più semplice del loop sull'output, non su come scrivere il ciclo. Detto questo, è anche una buona idea usare 'find .... | mentre leggi dir' invece di 'per dir in $ (...)', perché non deve fare l'intera ricerca prima di stampare nulla. – Cascabel

+0

@Jefromi: Piping 'find' in' while' gestisce anche i nomi con spazi correttamente gratuitamente. –

+0

Bene, evitare i loop non è ovviamente un "requisito" difficile, ma speravo che ci sarebbe stato un modo più semplice + intuitivo per farlo senza loop. Grazie per il ritrovamento | mentre le informazioni però. – amol

0

Questo è ancora un ciclo, in quanto utilizza il comando filiale di sed:

find -depth -type d |sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D' 

Sulla base di uno script in info sed (uniq lavoro-alike).

Edit Ecco lo script sed scoppiata con i commenti (copiati da info sed e modificato):

# copy the pattern space to the hold space 
h 

# label for branch (goto) command 
:b 
# on the last line ($) goto the end of 
# the script (b with no label), print and exit 
$b 
# append the next line to the pattern space (it now contains line1\nline2 
N 
# if the pattern space matches line1 with the last slash and whatever comes after 
# it followed by a newline followed by a copy of the part before the last slash 
# in other words line2 is different from line one with the last dir removed 
# see below for the regex 
/^\(.*\)\/.*\n\1$/ { 
    # Undo the effect of 
    # the n command by copying the hold space back to the pattern space 
    g 
    # branch to label b (so now line2 is playing the role of line1 
    bb 
} 
# If the `N' command had added the last line, print and exit 
# (if this is the last line then swap the hold space and pattern space 
# and goto the end (b without a label) 
$ { x; b } 

# The lines are different; print the first and go 
# back working on the second. 
# print up to the first newline of the pattern space 
P 
# delete up to the first newline in the pattern space, the remainder, if any, 
# will become line1, go to the top of the loop 
D 

Ecco cosa la regex sta facendo:

  • / - avviare un modello
  • ^ - corrisponde all'inizio della riga
  • \( - avviare un gruppo di cattura (subexpression di riferimento posteriore)
  • .* - zero o più (*) di qualsiasi carattere (.)
  • \) - end gruppo cattura
  • \/ - una barra (/) (fuggito con \)
  • .* - zero o più di qualsiasi carattere
  • \n - un ritorno a capo
  • \1 - una copia di il riferimento posteriore (che in questo caso è qualsiasi cosa era tra l'inizio della riga e l'ultima barra)
  • $ - corrisponde alla fine della riga
  • / - porre fine al modello
+0

Il tuo suggerimento funziona alla grande, ma lo trovo difficile da capire.Grazie però, proverò a capire cosa significano le opzioni sed – amol

0

penso che si può guardare a tutte le directory e quindi reindirizzare l'output e utilizzare xargs per il conteggio dei file di numero per ogni sottodirectory, quando non c'è sottodirectory (xargs trovare SUBDIR tipo d | wc -l ... qualcosa del genere, non posso testare adesso) hai trovato una foglia.

Questo è ancora un ciclo però.

+0

Ah ... forse non ho spiegato che cosa significava "leaf" in modo corretto, intendevo "leaf dirs", quindi se tutti i dir fossero file, si qualificherebbe ancora come una "foglia" per il mio problema: – amol

+0

ouhla, i ' Sono stanco, cerco le directory sotto e wc -l == 0, questo dovrebbe essere il trucco ... – LB40

4

Se stai cercando qualcosa di visivo, tree -d è bello.

 
drinks 
|-- coke 
| |-- cherry 
| `-- diet 
|  |-- caffeine-free 
|  `-- cherry 
|-- juice 
| `-- orange 
|  `-- homestyle 
|   `-- quart 
`-- pepsi 
    |-- clear 
    `-- diet 
+0

Grazie, anche questo è utile (non per il mio problema attuale, ma buono a sapersi) Mi chiedo, l'albero è una aggiunta recente? Ricordo di aver bisogno di qualcosa del genere (era su RHEL al momento, ora su Ubuntu) circa un anno fa e ho finito per usare lo script su http://centerkey.com/tree. – amol

10

Se si desidera solo le directory foglia (directory che non contengono qualsiasi sub-directory), guarda this other question. La risposta anche explains it, ma in breve è:

find . -type d -links 2 
+1

Su Mac almeno, i collegamenti includono directory correnti, padre e sottodirectory ma anche file. Quindi questa soluzione funziona solo per le directory foglia vuota. –

+0

Ho appena notato che non funziona anche su montature SMB. Ma lo fa su un supporto NFS. Ma nelle situazioni in cui funziona (directory locali o NFS Linux), è sicuramente la soluzione più semplice. – mivk

0

Provate il seguente one-liner (testato su Linux & OS X):

find . -type d -execdir sh -c 'test -z "$(find "{}" -mindepth 1 -type d)" && echo $PWD/{}' \; 
Problemi correlati