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).
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
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 Zeichenfind -empty
druckt die leeren Verzeichnisse und Dateienfind -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 ...
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).
Versuchen:
if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
# 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
}
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
DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
echo 'There is something alive in here'
fi
Könnten Sie die Ausgabe davon vergleichen?
ls -A /some/dir | wc -l
# Ü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:
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.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.test
-Implementierung unterstützt das -e
-Flag nicht.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
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
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 bash
dash
posh
busybox 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 busybox
sh
, 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).
Ich weiß, dass die Frage für bash markiert wurde. aber nur als Referenz für zsh Benutzer:
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.
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.
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.
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
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".
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.
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
if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi;
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.
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
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
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.
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
Einfache Antwort mit bash :
if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi;
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