Warum wird die for-Schleife im Verzeichnis nicht ausgeführt?

Warum wird die for-Schleife im Verzeichnis nicht ausgeführt?

Im folgenden Skript wird der erstefürSchleife wird wie erwartet ausgeführt, die zweite jedoch nicht. Ich erhalte keine Fehlermeldung, es scheint, als ob das Skript einfach hängen bleibt.

HOME=/root/mydir

DIR=$HOME/var
DIRWORK=$HOME/Local

for f in $(find $DIR -type f); do

    lsof -n $f | grep [a-z] > /dev/null

    if [ $? != 0 ]; then
    echo "hi"       
    fi
done


for i in $(find $DIRWORK -type d -name work); do
    echo "2"
done

Antwort1

Ihr Skript ist gefährlich codiert.

Zunächst gehe ich davon aus, dass Sie die Bash-Shell verwenden, da Sie sie mit „/bash“ und „/for“ gekennzeichnet haben.

In meiner Antwort werde ich dieses großartigeBash-Anleitung, die wahrscheinlich beste Quelle zum Erlernen von Bash.

1)Benutzen Sie niemals einBefehlsersetzung, vonentwederArt, ohne Anführungszeichen. Hier gibt es ein großes Problem: die Verwendung einer Erweiterung ohne Anführungszeichen, um die Ausgabe in Argumente aufzuteilen.

Genauer gesagt, dies $(find $DIRWORK -type d -name work)und $(find $DIR -type f) wird durchlaufenWorttrennung, wenn also findeine Datei mit Leerzeichen im Namen, also „Dateiname“, gefunden wird, übergibt das Worttrennungsergebnis von Bash 2 Argumente, forüber die der Befehl iterieren kann, also eines für „Datei“ und eines für „Name“. In diesem Fall sollten Sie hoffen, dass Sie „Datei: Keine solche Datei oder kein solches Verzeichnis“ und „Name: Keine solche Datei oder kein solches Verzeichnis“ erhalten, anstatt diese möglicherweise zu beschädigen, wenn sie tatsächlich vorhanden sind.

2)Gemäß Konvention werden Umgebungsvariablen (PATH, EDITOR, SHELL, ...) und interne Shell-Variablen (BASH_VERSION, RANDOM, ...) vollständig groß geschrieben. Alle anderen Variablennamen sollten klein geschrieben werden. Da Variablennamen zwischen Groß- und Kleinschreibung unterscheiden, wird durch diese Konvention das versehentliche Überschreiben von Umgebungs- und internen Variablen vermieden.

Ihr $DIRWORK-Verzeichnis verstößt gegen diese Konvention und ist auch nicht in Anführungszeichen gesetzt. Wenn wir also zulassen DIRWORK='/path/to/dir1 /path/to/dir2', findwird in zwei verschiedenen Verzeichnissen gesucht, wenn $DIRWORK nicht in Anführungszeichen steht. Das Thema der Verwendung von Anführungszeichen ist in Bash sehr wichtig, daher sollten Sie „Anführungszeichen verwenden“.jedenErweiterung sowie alles, was möglicherweise ein Sonderzeichen enthalten könnte, z. B. "$var", "$@", "${array[@]}", "$(command)". Bash behandelt alles in einfachen Anführungszeichen als wörtlich. Lernen Sie den Unterschied zwischen ' und " und `. SieheZitate,Argumenteund vielleicht möchten Sie auch einen Blick auf diesen Link werfen:http://wiki.bash-hackers.org/syntax/words

Dies ist eine sicherere Version Ihres Skripts, deren Verwendung ich Ihnen stattdessen empfehle:

my_home="/root/mydir"

my_dir="$my_home/var"
dir_work="$my_home/Local"

while IFS= read -r -d '' f; do
    # I'm guessing that you also want to ignore stderr;
    # this is where the 2>&1 came from.
    if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
        echo "hey, I'm safer now!"
    fi
done < <(find "$dir_work" -type f -print0)


while IFS= read -r -d '' f; do
    echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)

Wie Sie sehen, IFSist die Variable auf leer gesetzt, wodurch verhindert wird, readdass führende und nachfolgende Leerzeichen aus einer Zeile entfernt werden. Der readBefehl verwendet eine leere Zeichenfolge ( -d '') als Trennzeichen, um zu lesen, bis er ein \0 erreicht. findmuss entsprechend geändert werden, daher verwendet er die -print0Option, seine Daten mit einem \0 statt einer neuen Zeile zu begrenzen – was erstaunlicherweise und böswillig Teil eines Dateinamens sein kann. Das Aufteilen einer solchen Datei mit \n in zwei Teile würde unseren Code beschädigen.

Vielleicht möchten Sie mehr lesen überProzesssubstitutionwenn Sie mein Skript nicht vollständig verstehen.

Die vorherige Antwort, die besagt, dass find ... | while read name; do ...; donezum Lesen der Ausgabe verwendet werden soll, findkann auch falsch sein. Die whileobige Schleife wird in einer neuen Subshell mit einer eigenen Kopie der Variablen ausgeführt, die vom übergeordneten Element kopiert wurden. Diese Kopie wird dann für alles verwendet, was Sie möchten. Wenn die whileSchleife beendet ist, wird die Kopie der Subshell verworfen und die ursprünglichen Variablen des übergeordneten Elements haben sich nicht geändert.

Wenn Sie beabsichtigen, einige Variablen innerhalb dieser whileSchleife zu ändern und sie anschließend im übergeordneten Element zu verwenden, sollten Sie das sicherere Skript oben verwenden, um Datenverlust zu verhindern.

Antwort2

Dieser Code

for i in $(find $DIRWORK -type d -name work); do
    echo "2"
done

führt zuerst diese Zeile aus

find $DIRWORK -type d -name work

warten Sie, bis finddie Ausführung abgeschlossen ist, nehmen Sie dann die Ausgabe und fügen Sie sie wieder in die forSchleife ein

for i in the output of find; do
    echo "2"
done

Erst dann wird die Ausführung der For-Schleife gestartet.

Wenn das Beenden also findsehr lange dauert, formuss die Schleife lange warten, bevor sie starten kann.

Versuchen Sie, den findBefehl in einer interaktiven Eingabeaufforderung zeitlich zu steuern

$ time find $DIRWORK -type d -name work

und sehen Sie, wie lange das dauert.


Beachten Sie auch: Sie sollten keine forSchleife verwenden, um Dateinamen zu durchlaufen. Verwenden Sie eine whileSchleife readwie diese:

find $DIRWORK -type d -name work | while read name; do
    echo "2"
done

LesenDasfür mehr Informationen.

Bonus: Dadurch wird die whileSchleife parallel zu ausgeführt find. Das bedeutet, dass die whileSchleife eine Iteration ausführt, sobald findeine Zeile ausgegeben wird. Sie muss nicht warten, bis finddie Ausführung abgeschlossen ist.

verwandte Informationen