Durchlaufen Sie alle Dateien mit einer bestimmten Erweiterung

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

Ich möchte jede Datei im aktuellen Ordner durchlaufen und prüfen, ob sie mit einer bestimmten Erweiterung übereinstimmt. Der obige Code funktioniert nicht, wissen Sie warum?

AR89
quelle
4
Was ist mit for i in $(ls *.java); do echo "do something with file $i"; done?
Speakr
Gibt es keine Möglichkeit, diese if-Anweisung zu beheben?
AR89
1
Sie vergleichen $imit der Literalzeichenfolge "* .java"; Eine Mustererweiterung wird hier nicht durchgeführt.
Chepner
Verwenden Sie if [[ $i == *.java ]]; then.., um die if-Anweisung so zu korrigieren, wie Sie sie haben (beachten Sie die doppelten [[]] s und die nicht zitierte * .java).
andere Typ
2
lsAnalysieren Sie nicht - akzeptieren Sie die Antwort von @ chepner
Glenn Jackman

Antworten:

201

Keine ausgefallenen Tricks nötig:

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

Der Guard stellt sicher, dass die Schleife beendet wird, wenn keine übereinstimmenden Dateien vorhanden sind, ohne zu versuchen, einen nicht vorhandenen Dateinamen zu verarbeiten *.java. In bash(oder Shells, die etwas Ähnliches unterstützen) können Sie die nullglobOption verwenden, um eine fehlgeschlagene Übereinstimmung einfach zu ignorieren und nicht in den Hauptteil der Schleife einzutreten.

shopt -s nullglob
for i in *.java; do
    ...
done
chepner
quelle
5
Am einfachsten ist es, ein weiteres Muster hinzuzufügen : for i in *.java *.cpp; do. Wenn Sie Muster in aktiviert erweitert bashmit shopt -s extglob, können Sie schreiben for i in *.@(java|cpp); do.
Chepper
8
Es wird, wenn es tatsächlich mit Dateien übereinstimmt. Sie müssen verwenden, shopt -s nullglobdamit ein nicht übereinstimmendes Muster zur leeren Sequenz erweitert wird, anstatt wörtlich behandelt zu werden.
Chepper
1
@zygimantus ja sollte es, solange das aktuelle Verzeichnis ist, von wo aus das Skript ausgeführt wird. Wenn Sie nicht in dem Verzeichnis sind, das Sie sein möchten, sollten Sie cdin dieses Verzeichnis gehen, bevor Sie die forSchleife starten
danielsdesk
1
@puk Es hängt von Ihren Shell-Optionen ab. Standardmäßig wird ein nicht übereinstimmendes Muster als Literalzeichenfolge behandelt. Sie können festlegen nullglob, dass es als leere Sequenz behandelt wird, oder failglobdass es als Fehler behandelt wird. (Wenn Sie beide setzen, failglobhat Vorrang.)
chepner
3
@chepner Es kann für Nicht-Experten nützlich sein, dies in der Antwort anzugeben, da jemand es kopieren, einfügen und feststellen kann, dass es nicht funktioniert. Ein perfektes Beispiel wäre jemand, der alle " .jpg" -Dateien in " .png" konvertiert, bevor er eine wichtige Funktion ausführt
puk
13

Die richtige Antwort lautet @ chepner's

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

Hier ist jedoch ein kleiner Trick, um zu überprüfen, ob ein Dateiname eine bestimmte Erweiterung hat:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done
umläute
quelle
Wie wäre es mit einer Variablen extstatt .java?
AR89
13

Rekursiv Unterordner hinzufügen,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done
luismartingil
quelle
anstatt find . -name "*.java" -type f -exec echo \{\} \; zu vermeiden, dass die Ausgabe vonfind
umläute
1
Und wenn Sie dies nicht tun, sollten Sie zumindest "$i"innerhalb der Schleife zitieren .
Tripleee
2
Dies funktioniert nicht, wenn eine der Dateien Leerzeichen enthält.
Codeforester
1
@codeforester Ich hatte genau dieses Problem und habe es mit der Methode aus dieser Antwort behoben
fsinisi90
7

Schleife durch alle Dateien mit der Endung: .img, .bin, .txtSuffix und die Dateinamen drucken:

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

Oder rekursiv (auch in allen Unterverzeichnissen zu finden):

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

Ich stimme den anderen Antworten bezüglich der richtigen Art und Weise zu, die Dateien zu durchlaufen. Das OP fragte jedoch:

Der obige Code funktioniert nicht, wissen Sie warum?

Ja!

Ein ausgezeichneter Artikel Was ist der Unterschied zwischen test, [und [[?] Erklärt ausführlich, dass Sie unter anderem den Befehl nicht verwenden können expression matchingoder pattern matchinginnerhalb des testBefehls (was eine Abkürzung für ist [)

Feature neuer Test [[alter Test [Beispiel

Mustervergleich = (oder ==) (nicht verfügbar) [[$ name = a *]] || echo "name beginnt nicht mit einem 'a': $ name"

Regulärer Ausdruck = ~ (nicht verfügbar) [[$ (Datum) = ~ ^ Fr \ ... \ 13]] && echo "Es ist Freitag, der 13.!"
passend

Dies ist der Grund, warum Ihr Skript fehlschlägt. Wenn das OP an einer Antwort mit der [[Syntax interessiert ist (was den Nachteil hat, dass sie nicht auf so vielen Plattformen wie der [Befehl unterstützt wird), würde ich meine Antwort gerne bearbeiten, um sie einzuschließen.

BEARBEITEN: Alle Protips zum Formatieren der Daten in der Antwort als Tabelle wären hilfreich!

user000001
quelle
2

Wie @chepner in seinem Kommentar sagt, vergleichen Sie $ i mit einer festen Zeichenfolge.

Um die Situation zu erweitern und zu korrigieren, sollten Sie [[]] mit dem regulären Ausdrucksoperator = ~ verwenden

z.B:

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

Die Regex rechts von = ~ wird gegen den Wert des linken Operators getestet und sollte nicht in Anführungszeichen gesetzt werden (Anführungszeichen werden nicht fehlerhaft sein, sondern mit einer festen Zeichenfolge verglichen und daher höchstwahrscheinlich fehlschlagen.)

Die obige Antwort von @chepner mit glob ist jedoch ein viel effizienterer Mechanismus.

Petechen
quelle
Wie wäre es mit einer Variablen extstatt .java?
AR89
1
ack, keine Notwendigkeit für einen regulären Ausdruck: if [[ $i == *.java ]]oder if [[ $i == *.$ext ]]. Aber analysieren Sie nicht ls
Glenn Jackman
1

Ich fand diese Lösung sehr praktisch. Es verwendet die -orOption in find:

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

Es werden die Dateien mit der Erweiterung finden tex, pngund pdf.

f0nzie
quelle