Wie lege ich Variablen über Find+Exec fest?

Wie lege ich Variablen über Find+Exec fest?

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

FILEexistiert und hat eine Größe größer als Null

Bei rekursiven Suchen funktioniert das allerdings nicht so einfach.

findwird, aber für eine so einfache Angelegenheit wie das Festlegen eines Booleschen Werts ist es für diesen Fall wirklich nicht geeignet. lskann 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, lsist dies Ihre beste Wahl – findmacht die Dinge nur komplizierter. Was Sie interessiert, sind Dateieigenschaften, nicht Dateispeicherorte. Hier lsglänzt es, und grepdas Pingen seiner Ausgabe ist ein Kinderspiel.

So einfach geht’s:

ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))

Das wird nur eingestellt, FLAGwenn 1eine Datei -versteckt .dotEingeschlossene Dateien - existiert in $LOCATIONoder in einem seiner Unterverzeichnisse mit der Größe Null. Andernfalls $FLAGist 0.

Antwort2

finderstellt einen neuen Kindprozess, wenn Sie dies tun -exec. Kein Kindprozess kann die Umgebung seines Elternteils ändern.

Sie könnten beispielsweise die betreffenden Dateinamen sammeln findund 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=1wird in den Instanzen von ausgeführt, die shvon 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 findeine 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 findAusgabe bei der ersten Gelegenheit kürzen. GNU find hat ein -quitPrädikat, und Sie können ihm auch einfach sagen, dass ein einzelnes Zeichen angezeigt werden soll. Im folgenden Snippet FLAGendet es leer, wenn es keine Übereinstimmung gibt:

FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)

Die folgende Variante setzt FLAGauf 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 findprint-Matches erstellen und nur die erste Zeile behalten; findwird an einemSIGPIPEnachdem headseine 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 findeingebaut dankGlob-QualifikationDer folgende Codeausschnitt stellt filesdie 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 globstarbzw. 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 findals durch Zeilenumbrüche getrennte Liste analysieren.

find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
  while read -r empty_file; do
  done
}

verwandte Informationen