Ich dachte, das wäre einfach – aber es erweist sich als komplexer als erwartet.
Ich möchte alle Dateien eines bestimmten Typs in einem Verzeichnis durchlaufen, also schreibe ich Folgendes:
#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done
Dies funktioniert solange es gibt mindestens eine übereinstimmende Datei im Verzeichnis. Wenn jedoch keine übereinstimmenden Dateien vorhanden sind, wird Folgendes angezeigt:
current file is *.zip
Ich habe dann versucht:
#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done
Während der Hauptteil der Schleife nicht ausgeführt wird, wenn keine Dateien vorhanden sind, wird von ls eine Fehlermeldung angezeigt:
ls: *.zip: No such file or directory
Wie schreibe ich a Schleife, die keine übereinstimmenden Dateien sauber verarbeitet?
Kommentare
- Fügen Sie
shopt -s nullglob
hinzu, bevor Sie die for-Schleife ausführen. - @cuolnglm: Gruseligerweise führt dies dazu, dass der Name des ausführenden Skripts anstelle einer leeren Liste in dieser RHEL5-Box (Bash 3.2.25) zurückgegeben wird, wenn ich
FILES=
ls * .zip; for fname in "${FILES}"...
funktioniert jedoch wie erwartet mitfor fname in *.zip ; do....
- Verwenden Sie
for file in *.zip
, nicht`ls ...`
. @cuonglm ' schlägt vor, dass*.zip
zu nichts erweitert wird, wenn das Muster nicht ' ist Nicht mit einer Datei übereinstimmen.ls
ohne Argumente listet das aktuelle Verzeichnis auf. - In dieser Frage wird erläutert, warum das Parsen der Ausgabe von
ls
im Allgemeinen erfolgen soll vermieden: Warum nichtls
analysieren? ; Siehe auch den Link oben auf dieser Seite zu BashGuide ' s ParsingLs Artikel. - Mögliches Duplikat von Warum verschluckt sich mein Shell-Skript an Leerzeichen oder anderen Sonderzeichen?
Antwort
In bash
können Sie die Option nullglob
so festlegen, dass ein Muster entsteht, das Übereinstimmungen nichts „verschwindet“, anstatt als Literalzeichenfolge behandelt zu werden:
shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done
Im POSIX-Shell-Skript überprüfen Sie einfach, ob fname
ist vorhanden (und überprüfen Sie gleichzeitig mit [ -f ]
, ob es sich um eine reguläre Datei (oder einen Symlink zur regulären Datei) handelt und nicht um andere Typen wie Verzeichnis / fifo / Gerät. .):
for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done
Ersetzen Sie [ -f "$fname" ]
durch [ -e "$fname" ] || [ -L "$fname ]
, wenn Sie Sie möchten alle (nicht ausgeblendeten) Dateien durchlaufen, deren Name unabhängig davon mit .zip
endet ihren Typ.
Ersetzen Sie *.zip
durch .*.zip .zip *.zip
, wenn Sie auch versteckte Dateien berücksichtigen möchten, deren Name auf .zip
.
Kommentare
-
shopt -s nullglob
funktionierte nicht für Ich unter Ubuntu 17.04, aber[ -f "$fname" ] || continue
hat gut funktioniert. - @koppor Es hört sich so an, als würden Sie ' nicht wirklich verwenden
bash
. - +1 für eine POSIX-Lösung.
Antwort
set ./* #set the arg array to glob results ${2+":"} [ -e "$1" ] && #if more than one result skip the stat "$1" printf "current file is %s\n" "$@" #print the whole array at once ###or### ${2+":"} [ -e "$1" ] && #same kind of test for fname #iterate singly on $fname var for array do printf "file is %s\n" "$fname" #print each $fname for each iteration done
In einem Kommentar erwähnen Sie hier das Aufrufen einer Funktion …
file_fn() if [ -e "$1" ] || #check if first argument exists [ -L "$1" ] #or else if it is at least a broken link then for f #if so iterate on "$f" do : something w/ "$f" done else command <"${1-/dev/null}" #only fail w/ error if at least one arg fi file_fn *
Antwort
Verwenden Sie find
export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \;
Sie MÜSSEN Ihre Shell-Funktion mit export -f
damit dies funktioniert. Jetzt führt find
bash
aus, das Ihre Shell-Funktion ausführt, und bleibt nur auf der aktuellen Dir-Ebene.
Kommentare
- Wird durch Unterverzeichnisse wiederholt und ich möchte eine Bash-Funktion (kein Skript) für die Übereinstimmungen aufrufen.
- @symcbean I ' wurden bearbeitet, um sie auf ein einziges Verzeichnis zu beschränken und Bash-Funktionen zu verarbeiten.
Antwort
Stattdessen von:
FILES=`ls *.zip`
Versuchen Sie:
FILES=`ls * | grep *.zip`
Auf diese Weise, wenn ls fehlschlägt (was es ist In Ihrem Fall wird die fehlgeschlagene Ausgabe erfasst und als leere Variable zurückgegeben.
current file is <---Blank Here
Sie können dem eine Logik hinzufügen, damit es „Nein“ zurückgibt Datei gefunden „
#!/bin/bash FILES=`ls * | grep *.zip` if [[ $? == "0" ]]; then for fname in "$FILES" ; do echo current file is $fname done else echo "No Files Found" fi
Wenn der vorherige Befehl erfolgreich war (mit einem Wert von 0 beendet), wird die aktuelle Datei gedruckt, andernfalls wird“ Nr Gefundene Dateien „
Kommentare
- I thi nk es ist eine schlechte Idee, einen weiteren Prozess hinzuzufügen (
grep
), anstatt zu versuchen, das Problem mit einem besseren Tool (find
zu beheben ) oder Ändern der relevanten Einstellung für die aktuelle Lösung (mitshopt -s nullglob
) - Gemäß dem Kommentar des OP ' In ihrem ursprünglichen Beitrag funktioniert die
shopt -s nullglob
nicht. Ich habefind
versucht, während ich meine Antwort überprüft habe, und es ist immer wieder fehlgeschlagen. Ich denke wegen der Exportsache, die Dani gesagt hat.