it-swarm-eu.dev

scorrendo i file di `ls` in bash Script di shell

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.

87
Daniel A. White

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
100
CoverosGene

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 nullcharacter, 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.

64
Shawn J. Goff

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;
14
john

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

5
Ole Tange

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
4
n3o

Perché non impostare IFS su una nuova riga, quindi acquisire l'output di lsin una matrice? Impostare IFS su newline dovrebbe risolvere i problemi con caratteri divertenti nei nomi dei file; usare lspuò 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 lspassato 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.

1
jetset