
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 find
eine 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'
, find
wird 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, IFS
ist die Variable auf leer gesetzt, wodurch verhindert wird, read
dass führende und nachfolgende Leerzeichen aus einer Zeile entfernt werden. Der read
Befehl verwendet eine leere Zeichenfolge ( -d ''
) als Trennzeichen, um zu lesen, bis er ein \0 erreicht.
find
muss entsprechend geändert werden, daher verwendet er die -print0
Option, 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 ...; done
zum Lesen der Ausgabe verwendet werden soll, find
kann auch falsch sein. Die while
obige 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 while
Schleife 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 while
Schleife 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 find
die Ausführung abgeschlossen ist, nehmen Sie dann die Ausgabe und fügen Sie sie wieder in die for
Schleife 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 find
sehr lange dauert, for
muss die Schleife lange warten, bevor sie starten kann.
Versuchen Sie, den find
Befehl 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 for
Schleife verwenden, um Dateinamen zu durchlaufen. Verwenden Sie eine while
Schleife read
wie diese:
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
LesenDasfür mehr Informationen.
Bonus: Dadurch wird die while
Schleife parallel zu ausgeführt find
. Das bedeutet, dass die while
Schleife eine Iteration ausführt, sobald find
eine Zeile ausgegeben wird. Sie muss nicht warten, bis find
die Ausführung abgeschlossen ist.