Iterare su directory in Bash

Ho una domanda fondamentale su come bash funziona, e una domanda pratica.

Fondamentale domanda: supponiamo che io sono in una directory che ha sottodirectory tre: a, b, e c.

gallina il codice

for dir in $(ls)
do 
    echo $dir
done

sputa fuori:

a b c
a b c
a b c

io.e, dir sempre memorizza un elenco di tutti i file e le directory nella mia cwd. La mia domanda è: perché nel mondo sarebbe conveniente? A mio parere è molto più utile e intuitiva per avere dir store ogni elemento alla volta, io.e vorrei avere uscita

a
b
c

Inoltre, come per una delle risposte – è sbagliato usare for dir in $(ls), ma quando uso for dir in $(ls -l) ho anche di più copie di a b c (più di quanto non ci sono le directory/file nella cwd). Perché è che?

La mia seconda domanda è pratico: come faccio a ciclo su tutte le directory (non i file!) nel mio cwd che iniziano con la maiuscola? Ho iniziato con

for dir in `ls -l W*`

ma questo non riesce perché a) il motivo in questione 1 e b) perché non escludere file. Suggerimenti apprezzato.

  • Lo snippet di codice non dà il risultato che si dice fa. Si prega di riprodurre il codice effettivo con più attenzione.
  • Hai ragione. Quando io per la prima volta cercando di questo codice stavo usando $(ls), ma quando ho scritto la domanda che ho cambiato $(ls) per $(ls -l) perché sembra essere come è fatto ovunque ho guardato. Ho dato per scontato che il risultato sarebbe lo stesso e sono sorpreso che non è così.
  • Perché sei sorpreso che l’uscita di ls -l è diverso rispetto ad appena il ls? Il parametro cambia il suo comportamento (tipo man ls per i dettagli): -l offre lunga di uscita, inclusi i permessi, la proprietà, la data etc. Questo è un grande no-no per analizzare, perché la sua uscita può variare enormemente a seconda dell’implementazione e il locale. ls -l è ciò che è possibile utilizzare la riga di comando, ma mai in uno script come questo. Se avete visto il codice che analizza la sua uscita nell’ambiente è necessario contattare l’autore e chiedere per essere risolto.
  • Bene, voglio dire che sono sorpreso dal fatto che il formato di uscita è così diverso. L’uscita del $(ls) caso si ottiene tre righe di a b c, mentre l’output di $(ls -l) produce tre righe, ognuna delle quali contenente l’output desiderato (a, seguita da b, seguita da c). Ho pensato che i comandi sono simili…? … così dovrebbe produrre ragionevolmente … simili? … di uscita.
  • Possibile duplicato di stackoverflow.com/questions/2107945/…
  • Una parte della mia domanda è identica a quella che hai linkato, ma ho chiesto altre due parti: una concettuale domanda circa l’output di ls e ho anche aggiunto il requisito directory che iniziano con un carattere specifico.

InformationsquelleAutor alexvas | 2013-04-06

 

3 Replies
  1. 37

    Mai e poi mai di analizzare l’output di ls come questo (Perché non si dovrebbe analizzare l’output di ls(1)).

    Inoltre, la sintassi è sbagliata. Non significa (), vuoi dire che $().

    Detto questo, per un loop sulla directory che iniziano con W si potrebbe fare (o utilizzare il find comando, invece, a seconda dello scenario):

    for path in /my/path/W*; do
        [ -d "${path}" ] || continue # if not a directory, skip
        dirname="$(basename "${path}")"
        do_stuff
    done

    Come per l’output che si ottiene dal male ls-loop, non deve apparire come tale. Questo è l’output atteso e dimostra perché non si desidera utilizzare il comando ls, in primo luogo:

    $ find
    .
    ./c
    ./a
    ./foo bar
    ./b
    
    $ type ls
    ls is hashed (/bin/ls)
    
    $ for x in $(ls); do echo "${x}"; done
    a
    b
    c
    foo
    bar
    • Grazie. Voglio sapere, però, dove la mia comprensione di for dir in $(ls) è sbagliato. Ho dato per scontato che ls restituisce un array di memorizzazione di file e directory nel mio cwd, e quindi for sarebbe loop su di loro. Perché questo non accada?
    • Non viene restituito un array, si stampa una stringa in output che si cattura con la $() sintassi. Più che altro non posso rispondere alla tua domanda, perché il ciclo for non dovrebbe stampare una matrice come hai postato, basato sullo scenario che hai menzionato.
    • Aggiornata la mia risposta per dimostrare che l’output in realtà dovrebbe guardare come si prevede, in primo luogo, si potrebbe desiderare di provare di nuovo.
    • Ah! Ma c’è una differenza tra come te e mi associo i nomi delle directory! Ho usato echo $dir e utilizzato echo ${dir}. Infatti, se io uso la sintassi poi mi output simile al tuo, e sono certo che se si utilizza la mia sintassi allora prendi la mia uscita. Cosa fanno le parentesi graffe che dire, poi?
    • No, sono equivalenti, in questo caso. Le parentesi graffe sono necessari in scenari come questo: foo=hello; echo "$fooworld"; echo "${foo}world" (nel caso di variabili nome ambiguo). Vi suggerisco di leggere la Advanced Bash Scripting Guide e il BashGuide per saperne di più circa i principi fondamentali, i trabocchetti e praticamente tutto il necessario da sapere su bash scripting.
    • Ho provato con $dir, ma io ancora non riesco ad ottenere il risultato che si descrivono. Sei sicuro di stare usando esattamente il codice che ti danno in questione?
    • mi dispiace aboout che, potrebbe essere il mio occhio era sensazione di giramento di testa, si è cancellato il commento….

  2. 10

    Questo dovrebbe funzionare:

    shopt -s nullglob   # empty directory will return empty list
    for dir in ./*/;do
        echo "$dir"         # dir is directory only because of the /after *
    done

    Per essere ricorsiva, in sottodirectory troppo, utilizzare globstar:

    shopt -s globstar nullglob
    for dir in ./**/;do
        echo "$dir" # dir is directory only because of the /after **
    done

    Si può fare @Adrian Frühwirths’ metodo ricorsivo per sub-directory utilizzando globstar troppo:

    shopt -s globstar
    for dir in ./**;do
        [[ ! -d $dir ]] && continue # if not directory then skip
        echo "$dir"
    done

    Da Manuale Bash:

    globstar

    Se impostato, il modello ‘**’ utilizzato in un nome di file di espansione del contesto
    partita di tutti i file e zero o più directory e sottodirectory. Se
    il modello è seguito da un ‘/’, solo le cartelle e le sottocartelle
    partita.

    nullglob

    Se impostata, Bash permette filename modelli che non corrispondono a file per espandere
    per una stringa null, piuttosto che se stessi.

  3. 0

    Beh, sai quello che stai vedendo non è quello che vi aspettate. Il risultato che si sta vedendo non è il comando echo, ma dal comando dir.

    Provare le seguenti:

    ls -1 | while read line; do 
    
       if [-d "$line" ] ; then 
          echo $line
       fi
    
    done
    
    
    for files in $(ls) ; do
    
       if [-d "$files" ] ; then 
          echo $files
       fi
    
    done

Lascia un commento