Qualcuno ha uno script di shell modello per fare qualcosa con ls
un elenco di nomi di directory e scorrere in ciascuno di essi e fare qualcosa?
Sto programmando di fare ls -1d */
per ottenere l'elenco dei nomi delle directory.
Qualcuno ha uno script di shell modello per fare qualcosa con ls
un elenco di nomi di directory e scorrere in ciascuno di essi e fare qualcosa?
Sto programmando di fare ls -1d */
per ottenere l'elenco dei nomi delle directory.
Risposte:
Modificato per non usare ls dove farebbe un glob, come suggerito da @ shawn-j-goff e altri.
Basta usare un for..do..done
loop:
for f in *; do
echo "File -> $f"
done
È possibile sostituire il *
con *.txt
o qualsiasi altro glob che restituisce un elenco (di file, directory o qualsiasi altra cosa), un comando che genera un elenco, ad esempio $(cat filelist.txt)
, o sostituirlo effettivamente con un elenco.
All'interno del do
ciclo, fai semplicemente riferimento alla variabile del ciclo con il prefisso del simbolo del dollaro (quindi $f
nell'esempio sopra). Puoi echo
farlo o fare qualsiasi altra cosa tu voglia.
Ad esempio, per rinominare tutti i .xml
file 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 stai usando 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
for
loop e una trappola tipica del tentativo di analizzare l'output di ls
. @ DanielA.Bianco, potresti considerare di non accettare questa risposta se non è stata utile (o è potenzialmente fuorviante), dal momento che come hai detto, stai agendo su directory. La risposta di Shawn J. Goff dovrebbe fornire una soluzione più solida e funzionante al tuo problema.
ls
output , non dovresti leggere l'output in un for
ciclo , dovresti usare $()
invece di `` and Use More Quotes ™ . Sono sicuro che @CoverosGene intendesse bene, ma è terribile.
ls
è ls -1 | while read line; do stuff; done
. Almeno quello non si romperà per gli spazi bianchi.
mv -v
te non serveecho "moved $x -> $t"
Usare l'output di ls
per ottenere nomi di file è una cattiva idea . Può portare a malfunzionamenti e persino a script pericolosi. Questo perché un nome di file può contenere qualsiasi carattere tranne /
ed il null
carattere, e ls
non utilizzare uno dei quei caratteri come delimitatori, quindi se un nome di file ha uno spazio o un ritorno a capo, si sarà ottenere risultati inaspettati.
Esistono due modi molto efficaci per scorrere i file. Qui, ho usato semplicemente echo
per dimostrare di fare qualcosa con il nome file; puoi usare qualsiasi cosa, però.
Il primo consiste nell'utilizzare le funzionalità di globbing native della shell.
for dir in */; do
echo "$dir"
done
La shell si espande */
in argomenti separati che il for
ciclo 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 si vuole andare in modo ricorsivo in sottodirectory, allora questo non farà a meno che il guscio ha alcune caratteristiche globbing estese (ad esempio bash
's globstar
. Se la shell non ha queste caratteristiche, o se si vuole garantire che lo script funziona su una varietà di sistemi, quindi l'opzione successiva è usare find
.
find . -type d -exec echo '{}' \;
Qui, il find
comando chiamerà echo
e gli passerà un argomento del nome file. Lo fa una volta per ogni file che trova. Come nell'esempio precedente, non è possibile analizzare un elenco di nomi di file; invece, un fileneame viene inviato completamente come argomento.
La sintassi -exec
dell'argomento sembra un po 'divertente. find
prende il primo argomento dopo -exec
e lo considera come programma da eseguire, e ogni argomento successivo, serve come argomento per passare a quel programma. Ci sono due argomenti speciali che -exec
devono essere visti. Il primo è {}
; questo argomento viene sostituito con un nome file find
generato dalle parti precedenti . Il secondo è ;
, che fa find
sapere che questa è la fine della lista di argomenti da passare al programma; find
ne ha bisogno perché puoi continuare con più argomenti che sono previsti find
e non destinati al programma exec'd. Il motivo \
è che anche la shell tratta;
specialmente - rappresenta la fine di un comando, quindi dobbiamo evitarlo in modo che la shell lo dia come argomento find
piuttosto che consumarlo per se stesso; un altro modo per far sì che la shell non la tratti in modo particolare è inserirla tra virgolette: ';'
funziona altrettanto bene sia \;
per questo scopo.
find
. C'è della magia che può essere fatta e persino i limiti percepiti di -exec
possono essere aggirati. L' -print0
opzione è anche utile per l'uso con xargs
.
Per i file con spazi all'interno devi 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 di ls:
for i in *; do echo "$i"; done;
\n
è insufficiente. Non è mai una buona idea raccomandare di analizzare l'output di ls
.
Se hai installato GNU Parallel http://www.gnu.org/software/parallel/ puoi farlo:
ls | parallel echo {} is in this dir
Per rinominare tutto .txt in .xml:
ls *.txt | parallel mv {} {.}.xml
Guarda il video introduttivo di GNU Parallel per saperne di più: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Perché non impostare IFS su una nuova riga, quindi acquisire l'output di ls
in un array? L'impostazione di IFS su newline dovrebbe risolvere i problemi con caratteri divertenti nei nomi dei file; l'utilizzo ls
può essere utile perché ha una funzione di ordinamento integrata.
(Durante i test ho riscontrato difficoltà nell'impostare IFS \n
ma l'impostazione su backline di nuova riga funziona, come suggerito altrove qui):
Ad esempio (supponendo che il ls
modello di ricerca desiderato sia passato $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 esempio per acquisire un elenco di file ordinati per data di creazione (dalla più vecchia alla più recente), il ls
comando è ls -t -U -r
.
\n
è insufficiente. Le uniche soluzioni valide utilizzano un ciclo for con espansione del percorso o find
.
È così che lo faccio, ma probabilmente ci sono modi più efficienti.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done