Scorri tutti i file con un'estensione specifica


109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Voglio scorrere ogni file nella cartella corrente e controllare se corrisponde a un'estensione specifica. Il codice sopra non funziona, sai perché?


4
Di cosa for i in $(ls *.java); do echo "do something with file $i"; done?
speakr

non c'è modo di aggiustare quell'istruzione if?
AR89

1
Stai confrontando $icon la stringa letterale "* .java"; l'espansione del pattern non viene eseguita qui.
chepner

Per correggere l'istruzione if così come la avete, usate if [[ $i == *.java ]]; then.. (notate il doppio [[]] se non quotato * .java).
quell'altro ragazzo il

2
Non analizzarels - accetta la risposta di @ chepner
glenn jackman

Risposte:


201

Non sono necessari trucchi fantasiosi:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

La guardia assicura che se non ci sono file corrispondenti, il ciclo terminerà senza tentare di elaborare un nome file inesistente *.java. In bash(o nelle shell che supportano qualcosa di simile), puoi usare l' nullglobopzione per ignorare semplicemente una corrispondenza fallita e non entrare nel corpo del ciclo.

shopt -s nullglob
for i in *.java; do
    ...
done

5
Il modo più semplice è quello di aggiungere un altro modello: for i in *.java *.cpp; do. Se hai i modelli estesi abilitati bashcon shopt -s extglob, puoi scrivere for i in *.@(java|cpp); do.
chepner

8
Lo farà se effettivamente corrisponde a qualsiasi file. È necessario utilizzare in shopt -s nullglobmodo che un modello non corrispondente si espanda alla sequenza vuota piuttosto che essere trattato letteralmente.
chepner

1
@zygimantus sì, dovrebbe, purché la directory corrente sia quella da cui viene eseguito lo script. Se non sei nella directory che desideri essere, dovresti andare cdin quella directory prima di avviare il forciclo
danielsdesk

1
@puk Dipende dalle opzioni della shell. Per impostazione predefinita, un modello non corrispondente viene trattato come una stringa letterale. È possibile impostarlo nullglobin modo che venga trattato come una sequenza vuota o impostato failglobin modo che venga trattato come un errore. (Se si impostano entrambi, failglobha la precedenza.)
chepner

3
@chepner Potrebbe essere utile per i non esperti averlo indicato nella risposta poiché qualcuno potrebbe copiarlo e incollarlo e trovarlo non funzionante. Un esempio perfetto potrebbe essere qualcuno che converte tutti i file " .jpg" in " .png" prima di eseguire una funzione cruciale
puk

13

la risposta corretta è di @ chepner

EXT=java
for i in *.${EXT}; do
    ...
done

tuttavia, ecco un piccolo trucco per verificare se un nome di file ha una determinata estensione:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done

come sarebbe con una variabile extinvece di .java?
AR89

13

Aggiungi ricorsivamente sottocartelle,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done

invece di find . -name "*.java" -type f -exec echo \{\} \; evitare errori di interpretazione dell'output difind
umläute

1
E se non lo fai, almeno dovresti citare "$i"all'interno del ciclo.
tripleee

2
Questo non funzionerà se uno qualsiasi dei file ha uno spazio vuoto nel nome.
codeforester

1
@codeforester ho avuto esattamente quel problema e l'ho risolto usando il metodo da questa risposta: askubuntu.com/a/343753/551184
fsinisi90

7

Il ciclo di tutti i file che terminano con: .img, .bin, .txtsuffisso e stampare il nome del file:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

Oppure in modo ricorsivo (trova anche in tutte le sottodirectory):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done

3

Sono d'accordo con le altre risposte riguardo al modo corretto di scorrere i file. Tuttavia l'OP ha chiesto:

Il codice sopra non funziona, sai perché?

Sì!

Un articolo eccellente Qual è la differenza tra test, [e [[?] Spiega in dettaglio che, tra le altre differenze, non puoi usare expression matchingo pattern matchingall'interno del testcomando (che è una scorciatoia per [)

Caratteristica nuovo test [[vecchio test [Esempio

Pattern matching = (o ==) (non disponibile) [[$ name = a *]] || echo "il nome non inizia con una 'a': $ name"

Espressione regolare = ~ (non disponibile) [[$ (date) = ~ ^ Ven \ ... \ 13]] && echo "È venerdì 13!"
accoppiamento

Quindi questo è il motivo per cui il tuo script fallisce. Se l'OP è interessato a una risposta con la [[sintassi (che ha lo svantaggio di non essere supportato su tante piattaforme quante è il [comando), sarei felice di modificare la mia risposta per includerla.

EDIT: qualsiasi suggerimento su come formattare i dati nella risposta come tabella sarebbe utile!


2

come dice @chepner nel suo commento stai confrontando $ i con una stringa fissa.

Per espandere e correggere la situazione dovresti usare [[]] con l'operatore regex = ~

per esempio:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

la regex a destra di = ~ viene verificata rispetto al valore dell'operatore della mano sinistra e non deve essere quotata, (quotata non sarà un errore ma verrà confrontata con una stringa fissa e quindi molto probabilmente fallirà "

ma la risposta di @chepner sopra usando glob è un meccanismo molto più efficiente.


come sarebbe con una variabile extinvece di .java?
AR89

1
ack, non c'è bisogno di un'espressione regolare: if [[ $i == *.java ]]o if [[ $i == *.$ext ]]. Ma non analizzare ls
glenn jackman

1

Ho trovato questa soluzione abbastanza utile. Utilizza l' -oropzione in find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Sarà trovare i file con l'estensione tex, pnge pdf.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.