Bash iterate Dateiliste, außer wenn leer

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 mit for 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 nicht ls 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 (mit shopt -s nullglob)
  • Gemäß dem Kommentar des OP ' In ihrem ursprünglichen Beitrag funktioniert die shopt -s nullglob nicht. Ich habe find versucht, während ich meine Antwort überprüft habe, und es ist immer wieder fehlgeschlagen. Ich denke wegen der Exportsache, die Dani gesagt hat.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.