2009-08-28 17:03:49 +0000 2009-08-28 17:03:49 +0000
101
101

looping through &007 risulta in bash shell script

C'è qualcuno che ha un template shell script per fare qualcosa con ls per un elenco di nomi di directory e looping attraverso ognuno di essi e fare qualcosa?

Ho intenzione di fare ls per ottenere l'elenco dei nomi delle directory.

Risposte (7)

120
120
120
2009-08-28 17:09:37 +0000

Edite di non usare ls dove un glob farebbe, come @shawn-j-goff e altri suggeriscono.

Usate semplicemente un loop for..do..done:

for f in *; do
  echo "File -> $f"
done

Potete sostituire il * con *.txt o qualsiasi altro glob che restituisce una lista (di file, directory, o qualsiasi altra cosa per quel che conta), un comando che genera una lista, ad es, $(cat filelist.txt), o sostituirlo effettivamente con una lista.

All'interno del loop do, ci si riferisce semplicemente alla variabile loop con il prefisso del segno del dollaro (quindi $f nell'esempio precedente). Potete echo o farci tutto quello che volete.

Per esempio, per rinominare tutti i file .xml nella directory corrente in .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

O ancora meglio, se usate Bash potete usare le espansioni dei parametri Bash piuttosto che generare un sottoguscio:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
69
69
69
2012-04-29 22:16:24 +0000

Usare l'uscita di ls per ottenere i nomi dei file è una cattiva idea . Può portare a malfunzionamenti e persino a script pericolosi. Questo perché un nome di file può contenere qualsiasi carattere eccetto / e il carattere null, e ls non usa nessuno di questi caratteri come delimitatori, quindi se un nome di file ha uno spazio o una nuova linea, si _ otterranno risultati inaspettati.

Ci sono due modi molto buoni per iterare sui file. Qui, ho usato semplicemente echo per dimostrare di fare qualcosa con il nome del file; si può usare qualsiasi cosa, però.

Il primo è usare le caratteristiche di globbing nativo della conchiglia.

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

La shell espande */ in argomenti separati che il ciclo for legge; anche se c'è uno spazio, una nuova linea o qualsiasi altro strano carattere nel nome del file, for vedrà ogni nome completo come un'unità atomica; non sta analizzando la lista in alcun modo.

Se volete andare ricorsivamente nelle sottodirectory, allora questo non funzionerà a meno che la vostra shell non abbia alcune caratteristiche di globbing estese (come bash di globstar. Se la vostra shell non ha queste caratteristiche, o se volete assicurarvi che il vostro script funzioni su una varietà di sistemi, allora la prossima opzione è quella di usare find.

find . -type d -exec echo '{}' \;

Qui, il comando find chiamerà echo e gli passerà un argomento del nome del file. Lo fa una volta per ogni file che trova. Come nell'esempio precedente, non c'è l'analisi di una lista di nomi di file; invece, un nome di file viene inviato completamente come argomento.

La sintassi dell'argomento -exec sembra un po’ buffa. find prende il primo argomento dopo -exec e lo tratta come il programma da eseguire, e ogni argomento successivo, lo prende come argomento da passare a quel programma. Ci sono due argomenti speciali che -exec deve vedere. Il primo è {}; questo argomento viene sostituito con un nome di file che le parti precedenti di find generano. Il secondo è ;, che fa sapere a find che questa è la fine della lista di argomenti da passare al programma; find ha bisogno di questo perché si può continuare con più argomenti che sono destinati a find e non sono destinati al programma eseguito. La ragione di [ Usare l'uscita dilsper ottenere i nomi dei file è una cattiva idea ]&003. Può portare a malfunzionamenti e persino a script pericolosi. Questo perché un nome di file può contenere qualsiasi carattere eccetto/e il caratterenull, els` non usa nessuno di questi caratteri come delimitatori, quindi se un nome di file ha uno spazio o una nuova linea, si _ otterranno risultati inaspettati.

Ci sono due modi molto buoni per iterare sui file. Qui, ho usato semplicemente echo per dimostrare di fare qualcosa con il nome del file; si può usare qualsiasi cosa, però.

Il primo è usare le caratteristiche di globbing nativo della conchiglia.

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

La shell espande */ in argomenti separati che il ciclo for legge; anche se c'è uno spazio, una nuova linea o qualsiasi altro strano carattere nel nome del file, for vedrà ogni nome completo come un'unità atomica; non sta analizzando la lista in alcun modo.

Se volete andare ricorsivamente nelle sottodirectory, allora questo non funzionerà a meno che la vostra shell non abbia alcune caratteristiche di globbing estese (come bash di globstar. Se la vostra shell non ha queste caratteristiche, o se volete assicurarvi che il vostro script funzioni su una varietà di sistemi, allora la prossima opzione è quella di usare find.

find . -type d -exec echo '{}' \;

Qui, il comando find chiamerà echo e gli passerà un argomento del nome del file. Lo fa una volta per ogni file che trova. Come nell'esempio precedente, non c'è l'analisi di una lista di nomi di file; invece, un nome di file viene inviato completamente come argomento.

La sintassi dell'argomento -exec sembra un po’ buffa. find prende il primo argomento dopo -exec e lo tratta come il programma da eseguire, e ogni argomento successivo, lo prende come argomento da passare a quel programma. Ci sono due argomenti speciali che -exec deve vedere. Il primo è {}; questo argomento viene sostituito con un nome di file che le parti precedenti di find generano. Il secondo è ;, che fa sapere a find che questa è la fine della lista di argomenti da passare al programma; find ha bisogno di questo perché si può continuare con più argomenti che sono destinati a find e non sono destinati al programma eseguito. La ragione di è che anche la shell tratta ; in modo speciale - rappresenta la fine di un comando, quindi dobbiamo sfuggirgli, in modo che la shell lo dia come argomento a find piuttosto che consumarlo per se stesso; un altro modo per far sì che la shell non lo tratti in modo speciale è metterlo tra virgolette: ';' funziona altrettanto bene di \; per questo scopo.

17
17
17
2009-08-28 17:26:27 +0000

Per i file con spazi all'interno dovrete assicurarvi di citare la variabile come:

for i in $(ls); do echo "$i"; done;

oppure, potete cambiare la variabile d'ambiente IFS (Input field separator):

IFS=$'\n';for file in $(ls); do echo $i; done

Infine, a seconda di quello che state facendo, potreste non aver bisogno nemmeno della ls:

for i in *; do echo "$i"; done;
5
5
5
2010-08-22 20:02:35 +0000

Se avete installato GNU Parallel http://www.gnu.org/software/parallel/ potete fare questo:

ls | parallel echo {} is in this dir

Per rinominare tutti i .txt in .xml:

ls *.txt | parallel mv {} {.}.xml
``` &001 


Guarda il video introduttivo di GNU Parallel per saperne di più [ http://www.youtube.com/watch?v=OpaiGYxkSuQ ](http://www.youtube.com/watch?v=OpaiGYxkSuQ)
4
4
4
2015-02-11 14:36:01 +0000

Giusto per aggiungere alla risposta di CoverosGene, ecco un modo per elencare solo i nomi della directory:

for f in */; do
  echo "Directory -> $f"
done
1
1
1
2012-05-10 22:36:39 +0000

Perché non impostare IFS su una nuova linea e poi catturare l'output di ls in un array? Impostare IFS su newline dovrebbe risolvere i problemi con caratteri divertenti nei nomi dei file; usare ls può essere piacevole perché ha una funzione di ordinamento integrata.

(Nei test ho avuto problemi ad impostare IFS su \n ma impostarlo su newline backspace funziona, come suggerito qui altrove):

Ad esempio (supponendo che il modello di ricerca ls desiderato sia passato in $1):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

Questo è particolarmente utile su OS X, ad esempio, per catturare un elenco di file ordinati per data di creazione (dal più vecchio al più recente), il comando ls è ls -t -U -r.

-3
-3
-3
2009-08-28 17:28:24 +0000

Questo è il modo in cui lo faccio, ma ci sono probabilmente modi più efficienti.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt