Script Bash: teste le répertoire vide


95

Je veux tester si un répertoire ne contient aucun fichier. Si tel est le cas, je vais ignorer certains traitements.

J'ai essayé ce qui suit:

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

Cela donne l'erreur suivante:

line 1: [: too many arguments

Y a-t-il une solution / alternative?


Réponses:


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

En outre, il serait intéressant de vérifier si le répertoire existe avant.


11
Ne pas utiliser &&et ||simultanément! Si echo "Not Empty"échoue, echo "Empty"va courir! Essayez echo "test" && false || echo "fail"! Oui, je sais que echocela ne va pas échouer, mais si vous modifiez une autre commande, vous serez surpris!
Uzsolt

4
Veuillez donner au moins un exemple lorsque le code ci-dessus ne fonctionnera pas. Concrètement, ce code est absolument correct. J'espère que le demandeur sera capable d'adapter cela à ses propres fins
Andrey Atapin

3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- Si vous voulez "marquer" les répertoires non vides avec un fichier créé nommé non-empty, échouera et affichera "Vide".
uzsolt

4
où est touch /emptyma ligne?
Andrey Atapin

7
Oh non! Il y a un problème très important avec ce code. Cela devrait être if [ -n "$(ls -A /path/to/dir)" ]; then... Merci de mettre à jour la réponse avant que quelqu'un ne colle ce code quelque part sur son serveur et qu'un pirate informatique détermine comment l'exploiter. Si ce /path/to/dirn'est pas vide, alors les noms de fichiers y sont passés en tant qu'arguments /bin/testauxquels il n'est clairement pas destiné. Il suffit d'ajouter l' -nargument, problème résolu. Merci!
Edward Ned Harvey

21

Pas besoin de compter quoi que ce soit ou des globes de shell. Vous pouvez également utiliser readen combinaison avec find. Si findla sortie est vide, vous retournerez false:

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

Cela devrait être portable.


Belle solution, mais je pense que vos echoappels reflètent un résultat erroné: lors de mon test (sous Cygwin), find . -mindepth 1 | readun code d'erreur 141 était indiqué dans un répertoire non vide et 0 dans un répertoire vide
Lucas Cimon

@LucasCimon Pas ici (macOS et GNU / Linux). Pour un répertoire non vide, readretourne 0, et pour un vide, 1.
slhck

1
PSA ne fonctionne pas avecset -o pipefail
Colonel Thirty Two

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

Correct et rapide. Agréable!
l0b0 le

4
Il a besoin de guillemets (2x) et que le test -nsoit correct et sûr (test avec un répertoire avec des espaces dans le nom, testez-le avec un répertoire non vide avec le nom '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin

1
@ivan_pozdeev Ce n'est pas vrai, du moins pour la recherche GNU. Vous pensez peut-être grep. serverfault.com/questions/225798/…
Vladimir Panteleev

Il pourrait être plus simple d'écrire find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .et de s'appuyer sur l'état de sortie de grep. Quelle que soit la façon dont vous le faites, c'est vraiment la bonne réponse à cette question.
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

4
Ça doit être ça, pas besoin d'analyser la sortie de ls, voyez si elle est vide ou pas. Utiliser find me fait penser à une surdose.
akostadinov

4

Cela fera le travail dans le répertoire de travail courant (.):

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

ou la même commande divisée sur trois lignes juste pour être plus lisible:

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

Il suffit de remplacer ls -1A . | wc -lpar ls -1A <target-directory> | wc -lsi vous devez l'exécuter sur un autre dossier cible.

Edit : j'ai remplacé -1apar -1A(voir commentaire @Daniel)


2
utiliser à la ls -Aplace. Certains systèmes de fichiers n'ont pas .et les ..liens symboliques selon les docs.
Daniel Beck

1
Merci @ Daniel, j'ai édité ma réponse après votre suggestion. Je sais que le "1" pourrait être supprimé aussi.
Ztank1013

2
Cela ne fait pas mal, mais c'est implicite si la sortie n'est pas envoyée à un terminal. Comme vous le dirigez vers un autre programme, c'est redondant.
Daniel Beck

-1est définitivement redondant. Même si lsvous n'imprimerez pas un élément par ligne au moment où il sera importé, cela n'affectera pas l'idée de vérifier si elle produit zéro ligne ou plus.
Victor Yarema

3

Utilisez le suivant:

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

De cette façon, vous êtes indépendant du format de sortie de ls. -mindepthignore le répertoire lui-même, -maxdepthempêche la défense récursive dans des sous-répertoires pour accélérer les choses.


Bien sûr, vous êtes dépendant maintenant wc -let le findformat de sortie (qui est raisonnablement clair que).
Daniel Beck

3

Utiliser un tableau:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi

3
Très bonne solution, mais notez que cela nécessiteshopt -s nullglob
xebeche le

3
L' ${#files[@]} == 2hypothèse ne correspond pas au répertoire racine (vous ne testerez probablement pas s'il est vide, mais il est possible que du code ne sachant pas cette limitation).
ivan_pozdeev

3

Un hacky, mais uniquement bash, sans PID:

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

Ceci tire parti du fait que les commandes testinternes construites avec 2 si plus d'un argument est donné après -e: Tout d'abord, "$1"/*glob est développé par bash. Cela se traduit par un argument par fichier. Alors

  • S'il n'y a pas de fichiers, l'astérisque test -e "$1"*ne se développe pas, donc Shell retourne à l'essai du fichier nommé *, qui renvoie 1.

  • ... sauf s'il y a un fichier nommé exactement *, alors l'astérisque se développe pour bien, astérisque, qui se termine comme le même appel que ci-dessus, c'est-à-dire. test -e "dir/*", cette fois, retourne 0. (Merci @TrueY pour l'avoir signalé.)

  • S'il y a un fichier, test -e "dir/file"est exécuté, ce qui retourne 0.

  • Mais s'il y a plus de fichiers que 1, test -e "dir/file1" "dir/file2" est exécuté, ce qui est signalé par bash comme une erreur d'utilisation, c'est-à-dire 2.

case englobe toute la logique de sorte que seul le premier cas, avec 1 état de sortie, soit signalé comme ayant abouti.

Problèmes possibles que je n'ai pas vérifiés:

  • Il y a plus de fichiers que le nombre d'arguments autorisés - je suppose que cela pourrait se comporter de la même manière que le cas de 2 fichiers ou plus.

  • Ou alors il y a un fichier avec un nom vide - je ne suis pas sûr que ce soit possible avec n'importe quel système d'exploitation / système d'exploitation.


1
Correction mineure: s'il n'y a pas de fichier dans le répertoire /, alors test -e dir/*est appelé. Si le seul fichier est '*' dans le répertoire, le test retournera 0. S'il y a plus de fichiers, il en retournera 2. Donc, cela fonctionne comme décrit.
TrueY

Vous avez raison, @TrueY, je l'ai incorporé à la réponse. Merci!
Alois Mahdal

2

Avec FIND (1) (sous Linux et FreeBSD), vous pouvez regarder de manière non récursive une entrée de répertoire via "-maxdepth 0" et tester si elle est vide avec "-empty". Appliqué à la question cela donne:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi

Ce n'est peut-être pas 100% portable, mais c'est élégant.
Craig Ringer

1

Je pense que la meilleure solution est:

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

grâce à https://stackoverflow.com/a/91558/520567

Ceci est une édition anonyme de ma réponse qui pourrait être utile ou non à quelqu'un: Une légère modification indique le nombre de fichiers:

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

Cela fonctionnera correctement même si les noms de fichiers contiennent des espaces.


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

EDIT: Je pense que cette solution fonctionne bien avec gnu find, après un rapide aperçu de la mise en œuvre . Mais cela peut ne pas fonctionner avec, par exemple, la recherche de netbsd . En effet, celui-ci utilise le champ st_size de stat (2). Le manuel le décrit comme:

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.

Une meilleure solution, également plus simple, est:

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

De plus, la solution -une dans la 1ère solution est inutile.

EDIT: no -exit for gnu find .. la solution ci-dessus est bonne pour la recherche de NetBSD. Pour GNU find, cela devrait fonctionner:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi

find from GNU findutils 4.6.0 (la dernière version) n'a pas de -exitprédicat .
Dennis

0

Ce sont toutes d'excellentes choses - il suffit d'en faire un script pour que je puisse rechercher des répertoires vides sous le répertoire actuel. Le fichier ci-dessous devrait être placé dans un fichier nommé 'findempty', placé quelque part dans le chemin afin que bash puisse le trouver, puis exécuter chmod 755. Peut facilement être adapté à vos besoins spécifiques, je suppose.

#!/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

0

Ce travail pour moi, pour vérifier et traiter les fichiers dans le répertoire ../IN, considérant que le script est dans le ../Scriptrépertoire:

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

0

Qu'en est-il de tester si le répertoire existe et non vide dans une instruction if

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

-1

Pour tout répertoire autre que celui en cours, vous pouvez vérifier s'il est vide en le tentant rmdir, car il rmdirest garanti que les répertoires non vides échouent. Si cela rmdirréussit et que vous vouliez réellement que le répertoire vide survive au test, mkdirrecommencez.

N'utilisez pas ce hack s'il y a d'autres processus qui pourraient être perturbés par un répertoire dont ils savent qu'ils cessent brièvement d'exister.

Si rmdircela ne fonctionne pas pour vous et que vous testez peut-être des répertoires pouvant contenir un grand nombre de fichiers, toute solution reposant sur l'écorchage du shell peut être lente et / ou respecter les limites de longueur de ligne de commande. Probablement préférable d'utiliser finddans ce cas. La findsolution la plus rapide à laquelle je peux penser va comme

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

Cela fonctionne pour les versions GNU et BSD de findSolaris, mais pas pour celle de Solaris, qui manque à chacun de ces findopérateurs. Aimez votre travail, Oracle.


Pas une bonne idée. L'OP voulait simplement tester si le répertoire était vide ou non.
roaima

-3

Vous pouvez essayer de supprimer le répertoire et attendre une erreur. rmdir ne supprimera pas le répertoire s'il n'est pas vide.

_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

3
-1 C'est le genre de code qui se retourne contre nous. rmdiréchouera si je n'ai pas la permission de supprimer le répertoire; ou s'il s'agit d'un sous-volume Btrfs; ou s'il appartient à un système de fichiers en lecture seule. Et si rmdirn'échoue pas et mkdirfonctionne: que se passe-t-il si le répertoire déjà supprimé appartient à un autre utilisateur? Qu'en est-il de ses autorisations (éventuellement non standard)? ACL? attributs étendus? Tout perdu.
Kamil Maciorowski

Eh bien, je suis en train d'apprendre bash et j'ai pensé que cela pourrait être un moyen plus rapide plutôt que de parcourir tout le répertoire, mais les processeurs sont puissants et vous avez raison, pas sécurisés.
Impxd
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.