Qualcuno ha uno script di template Shell per fare qualcosa con ls
per un elenco di nomi di directory e il loro looping e fare qualcosa?
Sto pensando di fare ls -1d */
per ottenere l'elenco dei nomi delle directory.
Modificato per non usare ls dove farebbe un glob, come suggerito da @ shawn-j-goff e altri.
Basta usare un ciclo for..do..done
:
for f in *; do
echo "File -> $f"
done
È possibile sostituire *
con *.txt
o qualsiasi altro glob che restituisce una lista (di file, directory o qualsiasi cosa per quella materia), un comando che genera una lista, ad es. $(cat filelist.txt)
, o in realtà sostituirla con una lista.
All'interno del ciclo do
, si fa semplicemente riferimento alla variabile loop con il prefisso del simbolo del dollaro (quindi $f
nell'esempio precedente). Puoi echo
it o fare qualsiasi altra cosa desideri.
Ad 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 usi Bash puoi usare le espansioni dei parametri di Bash invece di generare una subshell:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
Usare l'output di ls
per ottenere nomi di file è una cattiva idea . Può portare a script malfunzionanti e persino pericolosi. Questo perché un nome file può contenere qualsiasi carattere tranne /
e null
character, e ls
non utilizza nessuno dei due caratteri come delimitatori, quindi se un nome file ha uno spazio o una nuova riga, you will ottiene risultati imprevisti.
Ci sono due ottimi modi per iterare sui file. Qui, ho usato semplicemente echo
per dimostrare di fare qualcosa con il nome del file; puoi usare qualsiasi cosa, però.
Il primo è usare le caratteristiche globbing native di Shell.
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 riga o qualsiasi altro carattere strano nel nome del file, for
vedrà ogni nome completo come un'unità atomica; non sta analizzando l'elenco in alcun modo.
Se vuoi andare in modo ricorsivo nelle sottodirectory, questo non funzionerà a meno che la tua shell non abbia alcune caratteristiche di globbing estese (come bash
'globstar
. Se la tua shell non ha queste funzionalità, o se vuoi assicurarti che il tuo script funzioni su una varietà di sistemi, quindi l'opzione successiva è 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 con l'esempio precedente, non c'è l'analisi di un elenco di nomi di file; invece, un fileneame viene inviato completamente come argomento.
La sintassi dell'argomento -exec
sembra un po 'divertente. find
prende il primo argomento dopo -exec
e considera che come programma da eseguire, e ogni argomento successivo, prende come argomento il passaggio a quel programma. Ci sono due argomenti speciali che -exec
deve vedere. Il primo è {}
; questo argomento viene sostituito con un nome file che genera le parti precedenti di find
. Il secondo è ;
, che consente a find
di sapere che questa è la fine dell'elenco di argomenti da passare al programma; find
ha bisogno di questo perché puoi continuare con più argomenti che sono intesi per find
e non destinati al programma exec'd. Il motivo per \
è che la shell tratta anche ;
appositamente - rappresenta la fine di un comando, quindi dobbiamo evitarlo in modo che Shell lo assegni come argomento a find
piuttosto che a consumarlo per se stesso; un altro modo per convincere la Shell a non trattarla in modo particolare è inserirla tra virgolette: ';'
funziona proprio come \;
per questo scopo.
Per i file con spazi all'interno dovrai assicurarti di citare la variabile come:
for i in $(ls); do echo "$i"; done;
oppure, è possibile modificare la variabile di ambiente IFS (input field separator):
IFS=$'\n';for file in $(ls); do echo $i; done
Infine, a seconda di cosa stai facendo, potresti non aver nemmeno bisogno della ls:
for i in *; do echo "$i"; done;
Se hai GNU Parallel http://www.gnu.org/software/parallel/ installed puoi farlo:
ls | parallel echo {} is in this dir
Per rinominare tutto .txt in .xml:
ls *.txt | parallel mv {} {.}.xml
Guarda il video introduttivo per GNU Parallelo per ulteriori informazioni: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Solo per aggiungere la risposta di CoverosGene, ecco un modo per elencare solo i nomi delle directory:
for f in */; do
echo "Directory -> $f"
done
Perché non impostare IFS su una nuova riga, quindi acquisire l'output di ls
in una matrice? Impostare IFS su newline dovrebbe risolvere i problemi con caratteri divertenti nei nomi dei file; usare ls
può essere bello perché ha una funzione di ordinamento integrata.
(Durante i test avevo problemi a impostare IFS su \n
ma impostarlo su newline backspace funziona, come suggerito altrove qui):
Per esempio. (assumendo il desiderato modello di ricerca ls
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
Ciò è particolarmente utile su OS X, ad es. Per acquisire un elenco di file ordinati per data di creazione (dal più vecchio al più recente), il comando ls
è ls -t -U -r
.