Meine Antwort lautet:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Es ist POSIX-kompatibel und nicht viel wichtiger als die Lösung, die das Verzeichnis auflistet und die Ausgabe an grep weiterleitet.
Verwendung:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
Ich mag die Antwort /unix//a/202276/160204 , die ich wie folgt umschreibe:
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
Es listet das Verzeichnis auf und leitet das Ergebnis an grep weiter. Stattdessen schlage ich eine einfache Funktion vor, die auf Glob-Erweiterung und Vergleich basiert.
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
Diese Funktion ist kein Standard-POSIX und ruft eine Subshell mit auf $()
. Ich erkläre diese einfache Funktion zuerst, damit wir die endgültige Lösung (siehe die Antwort von tldr oben) später besser verstehen können.
Erläuterung:
Die linke Seite (LHS) ist leer, wenn keine Erweiterung erfolgt. Dies ist der Fall, wenn das Verzeichnis leer ist. Die Option nullglob ist erforderlich, da andernfalls der Glob selbst das Ergebnis der Erweiterung ist, wenn keine Übereinstimmung vorliegt. (Wenn die RHS mit den Globs der LHS übereinstimmt, wenn das Verzeichnis leer ist, funktioniert dies nicht, da Fehlalarme auftreten, wenn ein LHS-Glob mit einer einzelnen Datei übereinstimmt, die als Glob selbst bezeichnet wird: Die *
im Glob entspricht der Teilzeichenfolge *
im Dateinamen. ) Der Klammerausdruck {,.[^.],..?}
deckt versteckte Dateien ab, aber nicht ..
oder .
.
Da shopt -s nullglob
es in $()
(einer Subshell) ausgeführt wird, ändert es nichts an der nullglob
Option der aktuellen Shell, was normalerweise eine gute Sache ist. Auf der anderen Seite ist es eine gute Idee, diese Option in Skripten festzulegen, da es fehleranfällig ist, wenn ein Glob etwas zurückgibt, wenn keine Übereinstimmung vorliegt. Man könnte also die Option nullglob am Anfang des Skripts setzen und sie wird in der Funktion nicht benötigt. Denken wir daran: Wir wollen eine Lösung, die mit der Option nullglob funktioniert.
Vorsichtsmaßnahmen:
Wenn wir keinen Lesezugriff auf das Verzeichnis haben, meldet die Funktion dasselbe, als ob es ein leeres Verzeichnis gäbe. Dies gilt auch für eine Funktion, die das Verzeichnis auflistet und die Ausgabe erfasst.
Der shopt -s nullglob
Befehl ist kein Standard-POSIX.
Es verwendet die von erstellte Subshell $()
. Es ist keine große Sache, aber es ist schön, wenn wir es vermeiden können.
Profi:
Nicht, dass es wirklich wichtig wäre, aber diese Funktion ist viermal schneller als die vorherige, gemessen an der CPU-Zeit, die im Kernel innerhalb des Prozesses verbracht wird.
Andere Lösungen:
Wir können das nicht POSIX entfernen shopt -s nullglob
Befehl auf der LHS und die Zeichenfolge setzen "$1/* $1/.[^.]* $1/..?*"
in der RHS und separat die Fehlalarme eliminieren , die auftreten , wenn wir nur Dateien mit dem Namen haben '*'
, .[^.]*
oder ..?*
in dem Verzeichnis:
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
Ohne den shopt -s nullglob
Befehl ist es jetzt sinnvoll, die Unterschale zu entfernen, aber wir müssen vorsichtig sein, da wir die Wortteilung vermeiden und dennoch eine Glob-Erweiterung auf der LHS zulassen möchten. Insbesondere das Zitieren zur Vermeidung von Wortaufteilungen funktioniert nicht, da es auch die Glob-Erweiterung verhindert. Unsere Lösung besteht darin, die Globs separat zu betrachten:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Wir haben immer noch eine Wortaufteilung für den einzelnen Glob, aber jetzt ist es in Ordnung, da dies nur dann zu einem Fehler führt, wenn das Verzeichnis nicht leer ist. Wir haben 2> / dev / null hinzugefügt, um die Fehlermeldung zu verwerfen, wenn viele Dateien mit dem angegebenen Glob auf der LHS übereinstimmen.
Wir erinnern uns, dass wir eine Lösung wollen, die auch mit der Nullglob-Option funktioniert. Die obige Lösung schlägt mit der Option nullglob fehl, da bei einem leeren Verzeichnis auch die LHS leer sind. Glücklicherweise heißt es nie, dass das Verzeichnis leer ist, wenn dies nicht der Fall ist. Es kann nur nicht gesagt werden, dass es leer ist, wenn es ist. Daher können wir die Nullglob-Option separat verwalten. Wir können die Fälle [ "$1/"* = "" ]
usw. nicht einfach hinzufügen, da diese als [ = "" ]
usw. erweitert werden und syntaktisch falsch sind. Also verwenden wir [ "$1/"* "" = "" ]
stattdessen etc. Wir müssen wieder die drei Fälle betrachten *
, ..?*
und .[^.]*
die versteckten Dateien passen, aber nicht .
und..
. Diese stören nicht, wenn wir die Option nullglob nicht haben, da sie auch nie sagen, dass sie leer ist, wenn sie nicht leer ist. Die endgültige vorgeschlagene Lösung lautet also:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Sicherheitsbedenken:
Erstellen Sie zwei Dateien rm
und x
in ein leeres Verzeichnis , und führen Sie *
auf der Eingabeaufforderung. Der Glob *
wird auf erweitert rm x
und ausgeführt, um ihn zu entfernen x
. Dies ist kein Sicherheitsbedenken, da sich in unserer Funktion die Globs dort befinden, wo die Erweiterungen nicht als Befehle, sondern als Argumente angesehen werden, genau wie in for f in *
.