it-swarm.com.de

Von einem Shell-Skript aus prüfen, ob ein Verzeichnis Dateien enthält

Wie überprüfe ich in einem Shell-Skript, ob ein Verzeichnis Dateien enthält?

Etwas Ähnliches

if [ -e /some/dir/* ]; then echo "huzzah"; fi;

was aber funktioniert, wenn das Verzeichnis eine oder mehrere Dateien enthält (die oben genannte funktioniert nur mit genau 0 oder 1 Dateien).

95
ionn

Die bisherigen Lösungen verwenden ls. Hier ist eine all-bash-Lösung:

#!/bin/bash
shopt -s nullglob dotglob     # To include hidden files
files=(/some/dir/*)
if [ ${#files[@]} -gt 0 ]; then echo "huzzah"; fi
61
Bruno De Fraine

Drei beste Tricks


shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))

Dieser Trick ist 100% bash und ruft eine Sub-Shell auf (erzeugt sie). Die Idee stammt von Bruno De Fraine und wurde durch teambob s Kommentar verbessert.

files=$(shopt -s nullglob dotglob; echo your/dir/*)
if (( ${#files} ))
then
  echo "contains files"
else 
  echo "empty (or does not exist or is a file)"
fi

Hinweis: Kein Unterschied zwischen einem leeren und einem nicht vorhandenen Verzeichnis (und selbst wenn der angegebene Pfad eine Datei ist).

Es gibt eine ähnliche Alternative und weitere Details (und Beispiele) zu 'official' FAQ für #bash IRC channel :

if (shopt -s nullglob dotglob; f=(*); ((${#f[@]})))
then
  echo "contains files"
else 
  echo "empty (or does not exist, or is a file)"
fi

[ -n "$(ls -A your/dir)" ]

Dieser Trick ist von nixCrafts Artikel aus dem Jahr 2007 inspiriert. Fügen Sie 2>/dev/null hinzu, um den Ausgabefehler "No such file or directory" zu unterdrücken.
Siehe auch Andrew Taylor s Antwort (2008) und gr8can8dian s Antwort (2011).

if [ -n "$(ls -A your/dir 2>/dev/null)" ]
then
  echo "contains files (or is a file)"
else
  echo "empty (or does not exist)"
fi

oder die einzeilige bashism Version:

[[ $(ls -A your/dir) ]] && echo "contains files" || echo "empty"

Hinweis: ls gibt $?=2 zurück, wenn das Verzeichnis nicht existiert. Aber kein Unterschied zwischen einer Datei und einem leeren Verzeichnis.


[ -n "$(find your/dir -Prune -empty)" ]

Dieser letzte Trick ist inspiriert von die Antwort von gravstar wobei -maxdepth 0 durch -Prune ersetzt und durch phils s Kommentar verbessert wird.

if [ -n "$(find your/dir -Prune -empty 2>/dev/null)" ]
then
  echo "empty (directory or file)"
else
  echo "contains files (or does not exist)"
fi

eine Variation mit -type d:

if [ -n "$(find your/dir -Prune -empty -type d 2>/dev/null)" ]
then
  echo "empty directory"
else
  echo "contains files (or does not exist or is not a directory)"
fi

Erklärung:

  • find -Prune ähnelt find -maxdepth 0 und verwendet weniger Zeichen
  • find -empty druckt die leeren Verzeichnisse und Dateien
  • find -type d druckt nur Verzeichnisse

Hinweis: Sie können [ -n "$(find your/dir -Prune -empty)" ] auch durch die folgende verkürzte Version ersetzen:

if [ `find your/dir -Prune -empty 2>/dev/null` ]
then
  echo "empty (directory or file)"
else
  echo "contains files (or does not exist)"
fi

Dieser letzte Code funktioniert in den meisten Fällen, aber beachten Sie, dass böswillige Pfade einen Befehl ausdrücken können ...

128
olibre

Wie wäre es mit dem folgenden:

if find /some/dir/ -maxdepth 0 -empty | read v; then echo "Empty dir"; fi

Auf diese Weise muss keine vollständige Liste der Inhalte des Verzeichnisses erstellt werden. Die Variable read dient sowohl zum Löschen der Ausgabe als auch zum Auswerten des Ausdrucks nur dann als wahr, wenn etwas gelesen wird (d. H. /some/dir/ wird von find leer gefunden).

47
mweerden

Versuchen:

if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
19
Greg Hewgill
# Works on hidden files, directories and regular files
### isEmpty()
# This function takes one parameter:
# $1 is the directory to check
# Echoes "huzzah" if the directory has files
function isEmpty(){
  if [ "$(ls -A $1)" ]; then
    echo "huzzah"
  else 
    echo "has no files"
  fi
}
14
gr8can8dian

Passen Sie auf Verzeichnisse mit vielen Dateien auf! Es kann einige Zeit dauern, den Befehl ls auszuwerten.

IMO die beste Lösung ist die, die verwendet 

find /some/dir/ -maxdepth 0 -empty
12
Gravstar
DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
     echo 'There is something alive in here'
fi
9
Andrew Taylor

Könnten Sie die Ausgabe davon vergleichen?

 ls -A /some/dir | wc -l
5
DGM
 # Überprüft, ob ein Verzeichnis nicht versteckte Dateien enthält .
 # 
 # Usage: if ispty "$ HOME"; dann Echo "Willkommen zu Hause"; fi 
 # 
 isempty () {
 für _ief in $ 1/*; tun
 wenn [-e "$ _ief"]; dann
 return 1 
 fi 
 erledigt
 return 0 
} 

Einige Implementierungshinweise:

  • Die for-Schleife vermeidet den Aufruf eines externen ls-Prozesses. Alle Verzeichniseinträge werden noch einmal gelesen. Dies kann nur durch das Schreiben eines C-Programms optimiert werden, das readdir () explizit verwendet.
  • Der test -e in der Schleife fängt den Fall eines leeren Verzeichnisses ein. In diesem Fall würde der Variablen _ief der Wert "somedir/*" zugewiesen. Nur wenn diese Datei existiert, wird die Funktion "nonleer" zurückgeben.
  • Diese Funktion funktioniert in allen POSIX-Implementierungen. Beachten Sie jedoch, dass Solaris/bin/sh nicht in diese Kategorie fällt. Die test-Implementierung unterstützt das -e-Flag nicht.
4
Roland Illig

Dies sagt mir, ob das Verzeichnis leer ist oder nicht, wie viele Dateien es enthält.

directory="/some/dir"
number_of_files=$(ls -A $directory | wc -l)

if [ "$number_of_files" == "0" ]; then
    echo "directory $directory is empty"
else
    echo "directory $directory contains $number_of_files files"
fi
3
Daishi

Dies kann eine sehr späte Antwort sein, aber hier ist eine Lösung, die funktioniert. Diese Zeile erkennt nur das Vorhandensein von Dateien! Wenn Verzeichnisse vorhanden sind, erhalten Sie keine falsche Bestätigung.

if find /path/to/check/* -maxdepth 0 -type f | read
  then echo "Files Exist"
fi
2
bitstreamer
dir_is_empty() {
   [ "${1##*/}" = "*" ]
}

if dir_is_empty /some/dir/* ; then
   echo "huzzah"
fi

Angenommen, Sie haben keine Datei mit dem Namen * in /any/dir/you/check, sie sollte mit bashdashposhbusybox sh und zsh funktionieren, jedoch (für zsh) unsetopt nomatch.

Die Performances sollten mit jeder ls vergleichbar sein, die * (glob) verwendet. Ich denke, dass dies bei Verzeichnissen mit vielen Knoten langsam sein wird (mein /usr/bin mit 3000+ Dateien ging nicht so langsam), wird zumindest Speicherplatz benötigen, um alle Verzeichnisse/Dateinamen zuzuweisen ( und mehr) Da sie alle als Argumente an die Funktion übergeben (aufgelöst) werden, haben einige Shell wahrscheinlich Grenzen für die Anzahl der Argumente und/oder die Länge der Argumente.

Ein tragbarer, schneller O(1) Weg, um zu überprüfen, ob ein Verzeichnis leer ist, wäre Nizza.

update

Die obige Version berücksichtigt keine versteckten Dateien/Verzeichnisse, falls weitere Tests erforderlich sind, wie z. B. der Code is_empty von Rich's sh (POSIX Shell) :

is_empty () (
cd "$1"
set -- .[!.]* ; test -f "$1" && return 1
set -- ..?* ; test -f "$1" && return 1
set -- * ; test -f "$1" && return 1
return 0 )

Stattdessen denke ich über so etwas nach:

dir_is_empty() {
    [ "$(find "$1" -name "?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" = "$1" ]
}

Einige Besorgnis über nachgestellte Schrägstriche Unterschiede zwischen dem Argument und der Suchausgabe, wenn das Verzeichnis leer ist, und nachgestellten Zeilenumbrüchen (dies sollte jedoch leicht zu handhaben sein), zeigen leider auf meiner busyboxsh, was wahrscheinlich einen Fehler in der find -> dd-Pipe mit der Ausgabe darstellt zufällig abgeschnitten (wenn ich cat verwendet habe, ist die Ausgabe immer gleich, scheint dd mit dem Argument count zu sein).

1
Alex

ZSH

Ich weiß, dass die Frage für bash markiert wurde. aber nur als Referenz für zsh Benutzer:

Test auf nicht leeres Verzeichnis

So prüfen Sie, ob foo nicht leer ist:

$ for i in foo(NF) ; do ... ; done

wenn foo nicht leer ist, wird der Code im for-Block ausgeführt.

Teste auf leeres Verzeichnis

Um zu überprüfen, ob foo leer ist:

$ for i in foo(N/^F) ; do ... ; done

wenn foo leer ist, wird der Code im for-Block ausgeführt.

Anmerkungen

Das oben angegebene Verzeichnis foo musste nicht zitiert werden. Dies ist jedoch möglich, wenn Folgendes erforderlich ist:

$ for i in 'some directory!'(NF) ; do ... ; done

Wir können auch mehrere Objekte testen, auch wenn es sich nicht um ein Verzeichnis handelt:

$ mkdir X     # empty directory
$ touch f     # regular file
$ for i in X(N/^F) f(N/^F) ; do echo $i ; done  # echo empty directories
X

Alles, was kein Verzeichnis ist, wird einfach ignoriert.

Extras

Da wir globbing sind, können wir jeden glob (oder Brace-Expansion) verwenden:

$ mkdir X X1 X2 Y Y1 Y2 Z
$ touch Xf                    # create regular file
$ touch X1/f                  # directory X1 is not empty
$ touch Y1/.f                 # directory Y1 is not empty
$ ls -F                       # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ for i in {X,Y}*(N/^F); do printf "$i "; done; echo  # print empty directories
X X2 Y Y2

Wir können auch Objekte untersuchen, die in einem Array platziert sind. Bei den Verzeichnissen wie oben zum Beispiel:

$ ls -F                       # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ arr=(*)                     # place objects into array "arr"
$ for i in ${^arr}(N/^F); do printf "$i "; done; echo
X X2 Y Y2 Z

So können wir Objekte testen, die möglicherweise bereits in einem Array-Parameter festgelegt sind.

Beachten Sie, dass der Code im for-Block offensichtlich in jedem Verzeichnis der Reihe nach ausgeführt wird. Wenn dies nicht erwünscht ist, können Sie einfach einen Array-Parameter auffüllen und diesen Parameter dann bearbeiten:

$ for i in *(NF) ; do full_directories+=($i) ; done
$ do_something $full_directories

Erläuterung

Für zsh-Benutzer gibt es das (F) glob-Qualifikationsmerkmal (siehe man zshexpn), das mit "vollen" (nicht leeren) Verzeichnissen übereinstimmt:

$ mkdir X Y
$ touch Y/.f        # Y is now not empty
$ touch f           # create a regular file
$ ls -dF *          # list everything in the current directory
f X/ Y/
$ ls -dF *(F)       # will list only "full" directories
Y/

Das Qualifikationsmerkmal (F) listet Objekte auf, die übereinstimmen mit: ist ein Verzeichnis UND ist nicht leer. (^F) stimmt also überein: Kein Verzeichnis OR ist leer. So würde (^F) alleine beispielsweise auch reguläre Dateien auflisten. Wie auf der Manpage zshexp erläutert, benötigen wir auch den (/) glob-Qualifier, der nur Verzeichnisse auflistet:

$ mkdir X Y Z
$ touch X/f Y/.f    # directories X and Y now not empty
$ for i in *(/^F) ; do echo $i ; done
Z

Um zu überprüfen, ob ein bestimmtes Verzeichnis leer ist, können Sie daher Folgendes ausführen:

$ mkdir X
$ for i in X(/^F) ; do echo $i ; done ; echo "finished"
X
finished

und nur um sicher zu gehen, dass ein nicht leeres Verzeichnis nicht erfasst wird:

$ mkdir Y
$ touch Y/.f
$ for i in Y(/^F) ; do echo $i ; done ; echo "finished"
zsh: no matches found: Y(/^F)
finished

Hoppla! Da Y nicht leer ist, findet zsh keine Übereinstimmungen für (/^F) ("leere Verzeichnisse") und gibt daher eine Fehlermeldung aus, dass keine Übereinstimmungen für den Glob gefunden wurden. Wir müssen diese möglichen Fehlermeldungen daher mit dem (N) glob-Qualifier unterdrücken:

$ mkdir Y
$ touch Y/.f
$ for i in Y(N/^F) ; do echo $i ; done ; echo "finished"
finished

Für nicht leere Verzeichnisse benötigen wir daher das Qualifikationsmerkmal (N/^F), das Sie wie folgt lesen können: "Benachrichtigen Sie mich nicht über Fehler, Verzeichnisse, die nicht voll sind".

In ähnlicher Weise benötigen wir für leere Verzeichnisse den Qualifizierer (NF), den wir auch lesen können: "Benachrichtigen Sie mich nicht über Fehler, vollständige Verzeichnisse".

1
Zorawar

Ausgehend von einem (oder mehreren) Hinweis auf die Antwort von olibre mag ich eine Bash-Funktion:

function isEmptyDir {
  [ -d $1 -a -n "$( find $1 -Prune -empty 2>/dev/null )" ]
}

Denn während es eine Subshell erzeugt, kommt es einer O(1) -Lösung so nahe, wie ich es mir vorstellen kann, und wenn man ihr einen Namen gibt, wird sie lesbar. Ich kann dann schreiben

if isEmptyDir somedir
then
  echo somedir is an empty directory
else
  echo somedir does not exist, is not a dir, is unreadable, or is  not empty
fi

Was O(1) betrifft, gibt es Ausreißerfälle: Wenn in einem großen Verzeichnis alle oder alle Einträge mit Ausnahme des letzten Eintrags gelöscht wurden, muss "find" möglicherweise das Ganze lesen, um festzustellen, ob es leer ist. Ich glaube, dass die erwartete Leistung O(1) ist, aber der schlimmste Fall ist linear in der Verzeichnisgröße. Ich habe das nicht gemessen.

1
ForDummies

Kleine Variation von Brunos Antwort :

files=$(ls -1 /some/dir| wc -l)
if [ $files -gt 0 ] 
then
    echo "Contains files"
else
    echo "Empty"
fi

Für mich geht das

1
loockass
if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi;
0
Toby

Ich bin überrascht, dass der wooledge-Guide zu leeren Verzeichnissen nicht erwähnt wurde. Dieses Handbuch und wirklich alle Wooledge ist ein Muss für Shell-Fragen.

Anmerkung von dieser Seite:

Versuchen Sie niemals, die Ausgabe von ls zu analysieren. Selbst ls-A-Lösungen können brechen (z. B. auf HP-UX, wenn Sie root sind, tut ls-A das genaue Gegenteil von dem, was Sie tun, wenn Sie nicht root sind - und nein, ich kann es nicht nachholen.) etwas das unglaublich blöd).

In der Tat möchte man vielleicht die direkte Frage ganz vermeiden. Normalerweise möchten die Leute wissen, ob eine Das Verzeichnis ist leer, weil sie die darin enthaltenen Dateien usw. verwenden möchten. Schauen Sie sich die größeren Dateien an Frage. Beispielsweise kann eines dieser findbasierten Beispiele eine geeignete Lösung sein:

   # Bourne
   find "$somedir" -type f -exec echo Found unexpected file {} \;
   find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \;  # GNU/BSD
   find "$somedir" -type d -empty -exec cp /my/configfile {} \;   # GNU/BSD

Meistens ist alles, was wirklich benötigt wird, etwa so:

   # Bourne
   for f in ./*.mpg; do
        test -f "$f" || continue
        mympgviewer "$f"
    done

Mit anderen Worten, die Person, die die Frage stellt, hat möglicherweise gedacht, dass ein expliziter Test des leeren Verzeichnisses .__ war. erforderlich, um eine Fehlermeldung wie mympgviewer zu vermeiden: ./*.mpg: Keine solche Datei oder Verzeichnis, wenn tatsächlich keine ein solcher Test ist erforderlich.

0
bishop

Ich würde für find gehen:

if [ -z "$(find $dir -maxdepth 1 -type f)" ]; then
    echo "$dir has NO files"
else
    echo "$dir has files"

Dadurch wird geprüft, ob im Verzeichnis nur nach Dateien gesucht wird, ohne die Unterverzeichnisse zu durchsuchen. Dann prüft es die Ausgabe mit der -z-Option von man test:

   -z STRING
          the length of STRING is zero

Sehen Sie einige Ergebnisse:

$ mkdir aaa
$ dir="aaa"

Leeres Verzeichnis:

$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

Nur dirs drin:

$ mkdir aaa/bbb
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

Eine Datei im Verzeichnis:

$ touch aaa/myfile
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
$ rm aaa/myfile 

Eine Datei in einem Unterverzeichnis:

$ touch aaa/bbb/another_file
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty
0
fedorqui

Mit einer gewissen Problemumgehung könnte ich einen einfachen Weg finden, um herauszufinden, ob sich Dateien in einem Verzeichnis befinden. Dies kann mit grep-Befehlen erweitert werden, um insbesondere XML-Dateien, TXT-Dateien usw. zu überprüfen. Beispiel: ls /some/dir | grep xml | wc -l | grep -w "0"

#!/bin/bash
if ([ $(ls /some/dir | wc -l  | grep -w "0") ])
    then
        echo 'No files'
    else
        echo 'Found files'
fi
0
chanaka777

Bisher habe ich keine Antwort mit grep gesehen, die meiner Meinung nach eine einfachere Antwort geben würde (mit nicht zu vielen sonderbaren Symbolen!). So würde ich prüfen, ob Mit der bourne-Shell Dateien im Verzeichnis vorhanden sind:

dies gibt die Anzahl der Dateien in einem Verzeichnis zurück:

ls -l <directory> | egrep -c "^-"

sie können den Verzeichnispfad eingeben, in den das Verzeichnis geschrieben wird. Die erste Hälfte der Pipe stellt sicher, dass das erste Zeichen der Ausgabe für jede Datei "-" ist. egrep zählt dann die Anzahl der Zeilen, die mit diesem Symbol beginnen, unter Verwendung regulärer Ausdrücke. Jetzt müssen Sie nur noch die erhaltene Nummer speichern und mit Anführungszeichen wie folgt vergleichen: 

 #!/bin/sh 
 fileNum=`ls -l <directory> | egrep -c "^-"`  
 if [ $fileNum == x ] 
 then  
 #do what you want to do
 fi

x ist eine Variable Ihrer Wahl.

0
Jecht Tyre

Versuchen Sie es mit dem Befehl find . Geben Sie das Verzeichnis fest oder als Argument ..__ an. Dann führen Sie find aus, um alle Dateien im Verzeichnis zu durchsuchen ..__

#!/bin/bash

_DIR="/home/user/test/"
#_DIR=$1
_FIND=$(find $_DIR -type f )
if [ -n "$_FIND" ]
then
   echo -e "$_DIR contains files or subdirs with files \n\n "
   echo "$_FIND"
else
echo "empty (or does not exist)"
fi
0
GiannakopoulosJ

Einfache Antwort mit bash :

if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi;
0

Ich mischte Prune-Sachen und letzte Antworten

find "$some_dir" -Prune -empty -type d | read && echo empty || echo "not empty"

das funktioniert auch für Wege mit Leerzeichen

0
Laurent G