Ich versuche, ein Bash-Skript zu schreiben, mit dem ich herausfinden möchte, ob ein bestimmter Speicherort leere Dateien enthält oder nicht, und eine E-Mail senden möchte, wenn eine Datei gefunden wird. Zuerst dachte ich darüber nach, „find“ und „mail“ zu kombinieren, aber wenn der Speicherort mehrere leere Dateien enthält, würde dies mehrere E-Mails senden, was ich nicht möchte. Daher dachte ich daran, eine Flag-Variable vor „find“ auf Null zu setzen und sie in „find“ auf 1 zu setzen, wenn eine leere Datei gefunden wird. Folgendes habe ich versucht:
FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG
aber das Problem ist, dass sich der Flag-Wert nicht auf 1 ändert, auch wenn der Speicherort leere Dateien enthält. Was mache ich falsch?
Antwort1
set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1
Das Shell-Builtin [ test ]
kann mit dem ize-Operator verwendet werden -s
. Von man test
:
-s FILE
FILE
existiert und hat eine Größe größer als Null
Bei rekursiven Suchen funktioniert das allerdings nicht so einfach.
find
wird, aber für eine so einfache Angelegenheit wie das Festlegen eines Booleschen Werts ist es für diesen Fall wirklich nicht geeignet. ls
kann rekursive Suchen genauso schnell durchführen, kann leicht so konfiguriert werden, dass die Dateigröße als erstes Feld für eine Auflistung bereitgestellt wird und nur ein Eintrag pro Zeile aufgelistet wird – garantiert. Wenn Sie den Booleschen Wert einer Shell-Variable basierend darauf festlegen möchten, ob eine rekursive Dateiauflistung eine Datei mit der Größe 0 enthält oder nicht, ls
ist dies Ihre beste Wahl – find
macht die Dinge nur komplizierter. Was Sie interessiert, sind Dateieigenschaften, nicht Dateispeicherorte. Hier ls
glänzt es, und grep
das Pingen seiner Ausgabe ist ein Kinderspiel.
So einfach geht’s:
ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))
Das wird nur eingestellt, FLAG
wenn 1
eine Datei -versteckt .dot
Eingeschlossene Dateien - existiert in $LOCATION
oder in einem seiner Unterverzeichnisse mit der Größe Null. Andernfalls $FLAG
ist 0
.
Antwort2
find
erstellt einen neuen Kindprozess, wenn Sie dies tun -exec
. Kein Kindprozess kann die Umgebung seines Elternteils ändern.
Sie könnten beispielsweise die betreffenden Dateinamen sammeln find
und dann in einem zweiten Durchgang die gewünschte E-Mail generieren:
find . -type f -size 0 -print >> /var/tmp/find.sz0
...
Antwort3
Dies export FLAG=1
wird in den Instanzen von ausgeführt, die sh
von aufgerufen werden find
. Die Umgebung eines Prozesses wird nie in seinen übergeordneten Prozess zurückkopiert. Ein Shell-Prozess, der nur eine Umgebungsvariable setzt und beendet wird, bewirkt nichts Dauerhaftes.
Wenn Sie testen möchten, ob find
eine beliebige Datei übereinstimmt, können Sie einfach testen, ob die Ausgabe leer ist. Um zu vermeiden, dass möglicherweise eine lange Liste von Dateien generiert wird, nur um sie zu verwerfen, sobald sich herausgestellt hat, dass sie nicht leer ist, können Sie die find
Ausgabe bei der ersten Gelegenheit kürzen. GNU find hat ein -quit
Prädikat, und Sie können ihm auch einfach sagen, dass ein einzelnes Zeichen angezeigt werden soll. Im folgenden Snippet FLAG
endet es leer, wenn es keine Übereinstimmung gibt:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
Die folgende Variante setzt FLAG
auf 0 oder 1:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0
Wenn Sie portabel bleiben möchten, können Sie find
print-Matches erstellen und nur die erste Zeile behalten; find
wird an einemSIGPIPEnachdem head
seine Seite des Rohrs geschlossen wurde.
if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
FLAG=1
else
FLAG=0
fi
Wenn dies nur ein vereinfachtes Beispiel war und Sie komplexere Variablen festlegen müssen, gibt es mehrere mögliche Ansätze. Ich werde nur einige gängige erwähnen.
Zsh hat viele Anwendungsfälle find
eingebaut dankGlob-QualifikationDer folgende Codeausschnitt stellt files
die Liste der Dateien ein, die mit übereinstimmen find $LOCATION -size 0 -type f
:
files=(**/*(.DL0))
Ksh und Bash haben auch rekursive Globs, wenn sie mit set -o globstar
bzw. aktiviert werden shopt -s globstar
. Beachten Sie jedoch, dass Bash bis 4.2 **
in symbolische Links zu Verzeichnissen rekursiv ist, was normalerweise nicht erwünscht ist. Sie können die weitere Verarbeitung in der Shell durchführen.
FIGNORE= # include dot files in wildcard matches (the ksh way)
GLOBIGNORE=.:.. # include dot files in wildcard matches (the bash way)
for x in **/*; do
done
Wenn keiner der übereinstimmenden Dateinamen Zeilenumbrüche enthält, können Sie die Ausgabe find
als durch Zeilenumbrüche getrennte Liste analysieren.
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}