2010-01-28 19 views
9

So che è possibile utilizzare il comando find per questo semplice lavoro. Ma ho ricevuto un incarico di non usare find o ls e fare il lavoro. Per favore aiuto ....Come elencare in modo ricorsivo le sottodirectory in bash senza utilizzare i comandi find o ls?

+0

Che cosa dovresti usare per fare questo? Il tuo script di shell, un programma C, Java ...? Se ci fai sapere cosa puoi * usare *, l'aiuto dovrebbe essere più immediato :-) – Grundlefleck

+0

Potresti essere in grado di usare 'echo *' o variazioni su quello per emulare 'ls'. Le directory vuote –

risposta

1

Il comando du elencherà le sottodirectory in modo ricorsivo.

io non sono sicuro se vuote directory ottengono una menzione, anche se

+0

vengono visualizzate per me utilizzando bash sul mio mac –

10

Prova utilizzando

tree -d 
+1

Perché ho ricevuto un -1? Commenta per favore. –

+0

Non ti ho dato il -1, ma per me è ovvio il motivo per cui l'hai preso. Perché non dare come risposta 'alias bla ls; bla'? La domanda che è stata posta era di scrivere l'algoritmo, non trovare qualche * comando * che faccia la stessa cosa di 'ls' ma non sia chiamato' ls'. – vladr

+7

@Vlad Romascanu: Oh ... ok, ma da "Ho ricevuto un incarico di non usare find o ls e fare il lavoro" ho capito che tutto era abbastanza buono, tranne find e ls ^^ –

1

come Mark Byers ha detto è possibile utilizzare echo * per ottenere un elenco di tutti i file nella corrente directory.

Il comando test o [] ha un opzione per verificare se un file è una directory.

Applicare la ricorsione e il gioco è fatto.

+1

In alternativa a' echo', 'for' sarà glob , così OP può usare 'per file in *; fare ... ; done'. – outis

25

si può fare con solo il guscio

#!/bin/bash 
recurse() { 
for i in "$1"/*;do 
    if [ -d "$i" ];then 
     echo "dir: $i" 
     recurse "$i" 
    elif [ -f "$i" ]; then 
     echo "file: $i" 
    fi 
done 
} 

recurse /path 

Oppure, se sei bash 4,0

#!/bin/bash 
shopt -s globstar 
for file in /path/** 
do 
    echo $file 
done 
+1

perché non si sottovaluta anche quello con il comando albero? o quello con eco, anche queste sono risposte valide. – ghostdog74

+0

Questo fallirà se una directory è vuota, cioè stamperà 'dir: bla/*' poiché 'bla/*' è visto come un letterale in quanto non ha discendenti. Puoi usare 'pushd/popd', o aggiungere' if [["$ 1/*" = "$ i"]]; poi continua; fi' come prima riga dopo 'do'. – Bogdan

0
$ function f { for i in $1/*; do if [ -d $i ]; then echo $i; f $i; fi; done } 
$ mkdir -p 1/2/3 2/3 3 
$ f . 
./1 
./1/2 
./1/2/3 
./2 
./2/3 
./3 
+1

-1 se fornisci una risposta completa a una domanda a casa, rendila corretta. per quanto riguarda gli spazi nei nomi delle voci di directory? –

+0

e non entra nelle directory e non elenca i file di testo – 18446744073709551615

+0

esso entra nelle directory e l'OP non dice nulla riguardo ai file di testo (o ai file oltre alle directory). Non ho intenzione di risolvere il problema delle 'directory con gli spazi'. –

4

Qui di seguito è una possibile implementazione:

# my_ls -- recursively list given directory's contents and subdirectories 
# $1=directory whose contents to list 
# $2=indentation when listing 
my_ls() { 
    # save current directory then cd to "$1" 
    pushd "$1" >/dev/null 
    # for each non-hidden (i.e. not starting with .) file/directory... 
    for file in * ; do 
    # print file/direcotry name if it really exists... 
    test -e "$file" && echo "$2$file" 
    # if directory, go down and list directory contents too 
    test -d "$file" && my_ls "$file" "$2 " 
    done 
    # restore directory 
    popd >/dev/null 
} 

# recursively list files in current 
# directory and subdirectories 
my_ls . 

Come esercizio puoi pensare a come modificare l'abov e script per stampare percorsi completi per i file (invece di file/nomi di directory solo indentati), possibilmente eliminando pushd/popd (e della necessità del secondo parametro $2) nel processo.

Per inciso, si noti l'uso di test XYZ && command che è del tutto equivalente a if test XYZ ; then command ; fi (cioè eseguire command se test XYZ riesce). Si noti inoltre che test XYZ è equivalente a [ XYZ ], vale a dire quanto sopra è equivalente a if [ XYZ ] ; then command ; fi. Si noti inoltre che qualsiasi punto e virgola ; può essere sostituito con una nuova riga, sono equivalenti.

Rimuovere la condizione test -e "$file" && (lasciare solo il echo) e vedere cosa succede.

Rimuovere le doppie virgolette intorno a "$file" e vedere cosa succede quando la directory di cui si sta elencando il contenuto contiene nomi di file con spazi al loro interno. Aggiungi set -x nella parte superiore dello script (o invocalo come sh -x scriptname.sh) per attivare l'output di debug e vedere cosa sta succedendo nei dettagli (per reindirizzare l'output di debug su un file, esegui sh -x scriptname.sh 2>debugoutput.txt).

Per elencare anche i file nascosti (ad esempio .bashrc):

... 
for file in * .?* ; do 
    if [ "$file" != ".." ] ; then 
    test -e ... 
    test -d ... 
    fi 
done 
... 

Si noti l'uso di != (confronto di stringhe) anziché -ne (confronto numerico.)

Un'altra tecnica sarebbe quella di generare subshells invece di usare pushd/popd:

my_ls() { 
    # everything in between roundbrackets runs in a separatly spawned sub-shell 
    (
    # change directory in sub-shell; does not affect parent shell's cwd 
    cd "$1" 
    for file in ... 
     ... 
    done 
) 
} 

noti che in alcune implementazioni shell v'è un limite rigido (~ 4k) sul numero di caratteri che può essere trasmesso come argomento per for (o per qualsiasi comando incorporato o esterno per quella materia). Poiché la shell expands, inline, * to a list of all matching filenames prima di eseguire effettivamente for su di esso, è possibile incontrare problemi se * viene espanso all'interno di una directory con molti file (stesso problema ti imbatterai durante l'esecuzione, ad esempio ls * nella stessa directory, e .es. ottenere un errore come Command too long.)

+0

puoi usare 'shopt -s dotglob' per elencare i file nascosti. – ghostdog74

+0

@ghostdog - non portatile. '. *' (o '.? *' per saltare '.' off the bat) fa il trucco bene ed è anche portatile. – vladr

0

Tecnicamente, né findls vengono utilizzati da find2perl | perl o File::Find direttamente.

 
$ find2perl -type d | perl 
$ perl -MFile::Find -e'find(sub{-d&&print"$File::Find::name\n"},".")' 
+0

ma poi, se questo è il caso, usando Python/Ruby/PHP ecc. Qualsiasi linguaggio in grado di elencare i file non sarà tecnicamente usando 'ls' o' find' – ghostdog74

+0

@ ghostdog74 Ovviamente. Ma dal momento che tutte le risposte serie hanno ricevuto feedback meno che sorprendenti, questo è qualcosa ... meno serio. (Stavo per scrivere un clone di 'find' in C e uno script di shell che lo compila + lo esegue, ma ho deciso che era troppo impegnativo per uno scherzo.) – ephemient

3

Dal momento che è per la bash, è una sorpresa che questo non è stato già detto:
(globstar valida dal bash 4.0+)

shopt -s globstar nullglob dotglob 
echo **/*/ 

Questo è tutto.
La barra finale / è lì per selezionare solo le directory.

L'opzione globstar attiva lo ** (ricerca in modo ricorsivo). L'opzione nullglob rimuove uno * quando non corrisponde a nessun file/dir. L'opzione dotglob include file che iniziano con un punto (file nascosti).

Problemi correlati