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?


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

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

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

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

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!


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.


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.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.