Bash-Scripting: Auf leeres Verzeichnis prüfen

95

Ich möchte testen, ob ein Verzeichnis keine Dateien enthält. In diesem Fall überspringe ich die Verarbeitung.

Ich habe folgendes versucht:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Das gibt den folgenden Fehler:

line 1: [: too many arguments

Gibt es eine Lösung / Alternative?

Anthony Kong
quelle
Related: stackoverflow.com/q/91368/52074
Trevor Boyd Smith

Antworten:

121
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Es wäre auch cool, vorher zu prüfen, ob das Verzeichnis existiert.

Andrey Atapin
quelle
11
Verwenden Sie nicht &&und ||gleichzeitig! Wenn echo "Not Empty"fehlschlägt, echo "Empty"wird ausgeführt! Versuchen Sie es echo "test" && false || echo "fail"! Ja, ich weiß, echowird nicht scheitern, aber wenn Sie einen anderen Befehl ändern, werden Sie überrascht sein!
Uzsolt
4
Bitte geben Sie mindestens ein Beispiel an, wenn der obige Code nicht funktioniert. Konkret ist dieser Code absolut korrekt. Ich hoffe, dass der Fragesteller dies für seine eigenen Zwecke anpassen kann
Andrey Atapin 31.10.11
3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- Wenn Sie die nicht leeren Verzeichnisse mit einer erstellten Datei mit dem Namen "markieren" möchten non-empty, schlägt dies fehl und es wird "Leer" gedruckt.
Uzsolt
4
wo steht touch /emptyin meiner leitung
Andrey Atapin
7
Ach nein! Es gibt ein sehr wichtiges Problem mit diesem Code. Es sollte sein if [ -n "$(ls -A /path/to/dir)" ]; then... Bitte aktualisieren Sie die Antwort, bevor jemand diesen Code irgendwo auf seinem Server einfügt und ein Hacker herausfindet, wie er ihn ausnutzen kann. Wenn /path/to/dirnicht leer, werden die Dateinamen dort als Argumente übergeben, an /bin/testdie eindeutig nicht gedacht ist. Fügen Sie einfach das -nArgument hinzu, das Problem ist gelöst. Vielen Dank!
Edward Ned Harvey
21

Keine Notwendigkeit, irgendetwas zu zählen oder Muschelkugeln. Sie können auch readin Kombination mit verwenden find. Wenn finddie Ausgabe leer ist, geben Sie Folgendes zurück false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

Dies sollte tragbar sein.

slhck
quelle
Schöne Lösung, aber ich denke, Ihre echoAnrufe spiegeln das falsche Ergebnis wider: In meinem Test (unter Cygwin) find . -mindepth 1 | readhatte ein 141 Fehlercode in einem nicht leeren Verzeichnis und 0 in einem leeren Verzeichnis
Lucas Cimon
@LucasCimon Nicht hier (macOS und GNU / Linux). Für ein nicht leeres Verzeichnis wird read0 und für ein leeres
Verzeichnis
1
PSA funktioniert nicht mitset -o pipefail
Colonel Zweiunddreißig
19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi
Uzsolt
quelle
Richtig und schnell. Nett!
10.
4
Es braucht Anführungszeichen (2x) und den Test -n, um korrekt und sicher zu sein (Test mit einem Verzeichnis mit Leerzeichen im Namen, Test mit einem nicht leeren Verzeichnis mit dem Namen '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin
1
@ivan_pozdeev Das stimmt zumindest für GNU nicht. Du denkst vielleicht an grep. serverfault.com/questions/225798/…
Vladimir Panteleev
Es könnte einfacher sein zu schreiben find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .und sich auf den Exit-Status von grep zu verlassen. Was auch immer Sie tun, dies ist die richtige Antwort auf diese Frage.
Tom Anderson
13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi
Renaud
quelle
4
Das muss es sein, keine Notwendigkeit für das Parsen von ls-Ausgaben, sehen Sie einfach, ob es leer ist oder nicht. Das Verwenden von find fühlt sich für mich einfach wie ein Overkill an.
Akostadinov
4

Dies erledigt die Arbeit im aktuellen Arbeitsverzeichnis (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

oder derselbe Befehl wurde in drei Zeilen aufgeteilt, um die Lesbarkeit zu verbessern:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Ersetzen Sie einfach ls -1A . | wc -lmit , ls -1A <target-directory> | wc -lwenn Sie benötigen sie auf einem anderen Zielordner auszuführen.

Edit : Ich habe ersetzt -1adurch -1A(siehe @ Daniel Kommentar)

ztank1013
quelle
2
Verwenden Sie ls -Astattdessen. Einige Dateisysteme nicht haben .und ..symbolische Links nach der docs.
Daniel Beck
1
Danke @Daniel, ich habe meine Antwort nach deinem Vorschlag bearbeitet. Ich weiß, dass die "1" auch entfernt werden könnte.
ztank1013
2
Es tut nicht weh, aber es ist impliziert, wenn es nicht an ein Terminal ausgegeben wird. Da Sie es an ein anderes Programm weiterleiten, ist es überflüssig.
Daniel Beck
-1ist definitiv überflüssig. Auch wenn lsbeim Pipe-Druck nicht ein Element pro Zeile gedruckt wird, hat dies keinen Einfluss auf die Idee, zu überprüfen, ob null oder mehr Zeilen erzeugt wurden.
Victor Yarema
3

Verwenden Sie Folgendes:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

Auf diese Weise sind Sie unabhängig vom Ausgabeformat von ls. -mindepthÜberspringt das Verzeichnis selbst und -maxdepthverhindert die rekursive Verteidigung in Unterverzeichnissen, um die Arbeit zu beschleunigen.

Daniel Beck
quelle
Natürlich sind Sie jetzt abhängig von wc -lund findAusgabeformat (was aber einigermaßen klar ist).
Daniel Beck
3

Verwenden eines Arrays:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
Glenn Jackman
quelle
3
Sehr schöne Lösung, aber beachten Sie, dass es erfordertshopt -s nullglob
Xebeche
3
Die ${#files[@]} == 2Annahme steht nicht für das Stammverzeichnis (Sie werden wahrscheinlich nicht testen, ob es leer ist, aber ein Code, der diese Einschränkung nicht kennt, könnte dies tun).
ivan_pozdeev
3

Eine hackige, aber nur bash-freie PID-Methode:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Dies macht sich die Tatsache zunutze, dass testbuiltin mit 2 endet, wenn nach mehr als einem Argument Folgendes angegeben wird -e: Zunächst wird "$1"/*glob um bash erweitert. Dies führt zu einem Argument pro Datei. Damit

  • Wenn keine Dateien vorhanden sind, wird das Sternchen in test -e "$1"*nicht erweitert, sodass Shell auf die angegebene Datei zurückgreift *, die 1 zurückgibt.

  • ... außer wenn es tatsächlich eine Datei gibt, die genau benannt ist *, wird das Sternchen zu "Sternchen" erweitert, was zum selben Aufruf wie oben führt, d. h. test -e "dir/*", nur dieses Mal wird 0 zurückgegeben. (Vielen Dank an @TrueY für den Hinweis.)

  • Wenn es eine Datei gibt, test -e "dir/file"wird ausgeführt, was 0 zurückgibt.

  • Wenn aber mehr Dateien als 1 vorhanden sind, test -e "dir/file1" "dir/file2" wird ausgeführt, was die Bash als Verwendungsfehler meldet, dh 2.

case Wickelt die gesamte Logik so um, dass nur der erste Fall mit 1 Exit-Status als Erfolg gemeldet wird.

Mögliche Probleme, die ich nicht überprüft habe:

  • Es gibt mehr Dateien als die Anzahl der zulässigen Argumente - ich schätze, dies könnte sich ähnlich verhalten wie bei 2+ Dateien.

  • Oder es gibt tatsächlich eine Datei mit einem leeren Namen - ich bin mir nicht sicher, ob dies unter einem vernünftigen OS / FS möglich ist.

Alois Mahdal
quelle
1
Kleine Korrektur: Wenn in dir / keine Datei vorhanden ist, test -e dir/*wird aufgerufen. Wenn die einzige Datei '*' in dir ist, gibt test 0 zurück. Wenn es mehr Dateien gibt, gibt es 2 zurück. So funktioniert es wie beschrieben.
TrueY
Du hast recht, @TrueY, ich habe es in die Antwort aufgenommen. Vielen Dank!
Alois Mahdal
2

Mit FIND (1) (unter Linux und FreeBSD) können Sie einen Verzeichniseintrag über "-maxdepth 0" nicht-rekursiv betrachten und mit "-empty" prüfen, ob er leer ist. Angewandt auf die Frage ergibt sich:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
quelle
Es ist möglicherweise nicht zu 100% portabel, aber elegant.
Craig Ringer
1

Ich denke die beste Lösung ist:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

Vielen Dank an https://stackoverflow.com/a/91558/520567

Dies ist eine anonyme Bearbeitung meiner Antwort, die möglicherweise für jemanden hilfreich ist oder nicht: Eine geringfügige Änderung gibt die Anzahl der Dateien an:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Dies funktioniert auch dann einwandfrei, wenn Dateinamen Leerzeichen enthalten.

akostadinov
quelle
1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: Ich denke, dass diese Lösung mit gnu find gut funktioniert, nach einem kurzen Blick auf die Implementierung . Dies funktioniert jedoch möglicherweise nicht, wenn beispielsweise netbsd gefunden wird . In der Tat wird das Feld st_size von stat (2) verwendet. Das Handbuch beschreibt es als:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Eine bessere und auch einfachere Lösung ist:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Auch der -prune in der 1. Lösung ist unbrauchbar.

EDIT: no -exit für gnu find .. die obige Lösung ist gut für NetBSD's find. Für GNU find sollte dies funktionieren:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
yarl
quelle
find from GNU findutils 4.6.0 (die neueste Version) hat kein -exitPrädikat .
Dennis
0

Das ist alles großartiges Zeug - habe es einfach in ein Skript geschafft, damit ich nach leeren Verzeichnissen unterhalb des aktuellen Verzeichnisses suchen kann. Das Folgende sollte in eine Datei namens 'findempty' geschrieben werden, die sich irgendwo im Pfad befindet, damit bash sie finden und dann chmod 755 ausführen kann. Kann leicht an Ihre spezifischen Bedürfnisse angepasst werden, denke ich.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi
Warren Sherliker
quelle
0

Diese Arbeit für mich, um Dateien im Verzeichnis zu prüfen und zu verarbeiten ../IN, wenn man bedenkt, dass sich das Skript im ../ScriptVerzeichnis befindet:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code
Arijit
quelle
0

Was ist mit dem Testen, ob ein Verzeichnis vorhanden und in einer if-Anweisung nicht leer ist?

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi
stanieviv
quelle
-1

Bei jedem anderen Verzeichnis als dem aktuellen können Sie überprüfen, ob es leer ist, indem Sie es versuchen rmdir, da dies rmdirbei nicht leeren Verzeichnissen garantiert fehlschlägt. Wenn dies rmdirgelingt und Sie tatsächlich wollten, dass das leere Verzeichnis den Test überlebt, mkdirwiederholen Sie es einfach .

Verwenden Sie diesen Hack nicht, wenn es andere Prozesse gibt, die durch ein Verzeichnis, von dem sie wissen, dass es kurzzeitig aufhört zu existieren, möglicherweise unübersichtlich werden.

Wenn dies bei rmdirIhnen nicht funktioniert und Sie möglicherweise Verzeichnisse testen, die möglicherweise eine große Anzahl von Dateien enthalten, kann jede Lösung, die auf Shell-Globbing beruht, langsam werden und / oder zu Längenbeschränkungen für die Befehlszeile führen. Wahrscheinlich besser findin diesem Fall zu verwenden. Die schnellste findLösung, die mir einfällt, geht so

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Dies funktioniert für die GNU- und BSD-Versionen, findjedoch nicht für die Solaris- Versionen , bei denen jeder einzelne dieser findOperatoren fehlt . Liebe deine Arbeit, Oracle.

flabdablet
quelle
Keine gute Idee. Das OP wollte nur testen, ob das Verzeichnis leer war oder nicht.
Roaima
-3

Sie können versuchen, das Verzeichnis zu entfernen und auf einen Fehler zu warten. rmdir löscht das Verzeichnis nicht, wenn es nicht leer ist.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi
impxd
quelle
3
-1 Dies ist die Art von Code, der fehlschlägt. rmdirschlägt fehl, wenn ich keine Berechtigung zum Entfernen des Verzeichnisses habe. oder wenn es ein Btrfs-Subvolume ist; oder wenn es zu einem schreibgeschützten Dateisystem gehört. Und wenn rmdirnicht fehlschlägt und mkdirläuft: Was, wenn das bereits entfernte Verzeichnis einem anderen Benutzer gehörte? Was ist mit seinen (möglicherweise nicht standardmäßigen) Berechtigungen? ACL? erweiterte Attribute? Alles verloren.
Kamil Maciorowski
Nun, ich lerne gerade bash und dachte, dass es ein schnellerer Weg sein könnte, als das gesamte Verzeichnis zu durchlaufen, aber CPUs sind leistungsfähig und Sie haben Recht, nicht sicher.
impxd