web-dev-qa-db-fra.com

Vérification à partir d'un script shell si un répertoire contient des fichiers

À partir d'un script Shell, comment vérifier si un répertoire contient des fichiers?

Quelque chose de semblable à ceci

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

mais qui fonctionne si le répertoire contient un ou plusieurs fichiers (le précédent ne fonctionne qu'avec exactement 0 ou 1 fichiers).

95
ionn

Les solutions utilisées jusqu'ici utilisent ls. Voici une solution tout bash:

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

Trois meilleures astuces


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

Cette astuce est 100% bash et appelle un sous-shell. L'idée est de Bruno De Fraine et améliorée par le commentaire de teambob .

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

Remarque: pas de différence entre un répertoire vide et un répertoire non existant (et même lorsque le chemin fourni est un fichier).

Il existe une alternative similaire et plus de détails (et plus d'exemples) sur le 'officiel' FAQ pour #bash IRC canal :

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)" ]

Cette astuce est inspirée de article de nixCraft posté en 2007. Ajoutez 2>/dev/null pour supprimer l'erreur de sortie "No such file or directory".
Voir également la réponse de Andrew Taylor (2008) et la réponse de gr8can8dian (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

ou la version bashiste à une ligne:

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

Remarque: ls renvoie $?=2 lorsque le répertoire n'existe pas. Mais pas de différence entre un fichier et un répertoire vide.


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

Cette dernière astuce est inspirée de réponse de gravstar-maxdepth 0 est remplacé par -Prune et amélioré par le commentaire de phils .

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

une variante utilisant -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

Explication:

  • find -Prune est similaire à find -maxdepth 0 en utilisant moins de caractères
  • find -empty imprime les répertoires et fichiers vides
  • find -type d imprime uniquement les répertoires

Remarque: Vous pouvez également remplacer [ -n "$(find your/dir -Prune -empty)" ] par la version abrégée ci-dessous:

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

Ce dernier code fonctionne dans la plupart des cas, mais sachez que des chemins malveillants peuvent exprimer une commande ...

128
olibre

Que diriez-vous de ce qui suit:

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

De cette façon, il n'est pas nécessaire de générer une liste complète du contenu du répertoire. La read permet à la fois de supprimer la sortie et de rendre l'expression évaluée à true uniquement lorsque quelque chose est lu (par exemple, /some/dir/ est trouvé vide par find).

47
mweerden

Essayer:

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

Attention aux répertoires avec beaucoup de fichiers! L'évaluation de la commande ls peut prendre un certain temps.

OMI la meilleure solution est celle qui utilise 

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

Pourriez-vous comparer la sortie de ceci?

 ls -A /some/dir | wc -l
5
DGM
 # Vérifie si un répertoire contient des fichiers non cachés .____ # 
 # # Usage: si vide "$ HOME"; puis echo "Bienvenue à la maison"; fi 
 # 
 isempty () {
 pour _ief en $ 1/*; faire
 si [-e "$ _ief"]; puis
 retourne 1 
 Fi
 terminé
 retourne 0 
} 

Quelques notes d'implémentation:

  • La boucle for évite un appel à un processus externe ls. Il lit toujours toutes les entrées du répertoire une fois. Cela ne peut être optimisé qu'en écrivant un programme C qui utilise explicitement readdir ().
  • Le test -e à l'intérieur de la boucle attrape le cas d'un répertoire vide, auquel cas la variable _ief se verra attribuer la valeur "somedir/*" Ce n'est que si ce fichier existe que la fonction retournera "non vide"
  • Cette fonction fonctionnera dans toutes les implémentations POSIX. Mais sachez que le Solaris/bin/sh ne fait pas partie de cette catégorie. Son implémentation test ne prend pas en charge l'indicateur -e.
4
Roland Illig

Cela me dit si le répertoire est vide ou pas, le nombre de fichiers qu’il contient.

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

Cela peut être une réponse très tardive, mais voici une solution qui fonctionne. Cette ligne ne reconnaît que l’existence de fichiers! Cela ne vous donnera pas un faux positif si des répertoires existent.

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

Supposons que vous n'avez pas un fichier nommé * dans /any/dir/you/check, il devrait fonctionner sur bashdashposhbusybox sh et zsh mais (pour zsh) nécessite unsetopt nomatch.

Les performances devraient être comparables à toutes les variables ls qui utilisent * (glob), je suppose que cela sera lent sur les répertoires avec beaucoup de noeuds (mon /usr/bin avec plus de 3000 fichiers ne sera pas si lent), utilisera au moins assez de mémoire pour allouer tous les noms de fichiers/répertoires et plus) puisqu'ils sont tous passés (résolus) à la fonction en tant qu'arguments, certains Shell ont probablement des limites sur le nombre d'arguments et/ou la longueur d'arguments.

Un moyen rapide rapide O(1) de zéro ressource pour vérifier si un répertoire est vide serait agréable à avoir.

mettre à jour

La version ci-dessus ne prend pas en compte les fichiers/répertoires cachés, au cas où des tests supplémentaires seraient nécessaires, comme le is_empty from from { astuces de Rich (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 )

Mais au lieu de cela, je pense à quelque chose comme ceci:

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

Certaines inquiétudes concernant les différences finales entre les barres obliques et l'argument et la sortie de recherche lorsque le répertoire est vide, et les nouvelles lignes (mais cela devrait être facile à gérer), malheureusement sur ma busyboxsh montrer ce qui est probablement un bogue sur le tube find -> dd avec la sortie tronqué de manière aléatoire (si j'ai utilisé cat la sortie est toujours la même, semble être dd avec l'argument count).

1
Alex

ZSH

Je sais que la question était marquée pour bash; mais, juste pour référence, pour zsh users:

Test du répertoire non vide

Pour vérifier si foo est non vide:

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

où, si foo n'est pas vide, le code du bloc for sera exécuté.

Test du répertoire vide

Pour vérifier si foo est vide:

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

où, si foo est vide, le code du bloc for sera exécuté.

Remarques

Nous n'avons pas eu besoin de citer le répertoire foo ci-dessus, mais nous pouvons le faire si nous avons besoin de:

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

Nous pouvons également tester plusieurs objets, même s'il ne s'agit pas d'un répertoire:

$ 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

Tout ce qui n'est pas un répertoire sera simplement ignoré.

Extras

Depuis que nous sommes en train de globbing, nous pouvons utiliser n’importe quel glob (ou expansion d’attelle):

$ 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

Nous pouvons également examiner les objets placés dans un tableau. Avec les répertoires ci-dessus, par exemple:

$ 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

Ainsi, nous pouvons tester des objets qui peuvent déjà être définis dans un paramètre de tableau.

Notez que le code du bloc for est évidemment exécuté sur chaque répertoire. Si cela n'est pas souhaitable, vous pouvez simplement renseigner un paramètre de tableau, puis agir sur ce paramètre:

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

Explication

Pour les utilisateurs zsh, il existe le qualificatif (F) glob (voir man zshexpn), qui correspond aux répertoires "complets" (non vides):

$ 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/

Le qualificatif (F) répertorie les objets qui correspondent à: est un répertoire ET n'est pas vide. Donc, (^F) correspond: aucun répertoire OR n'est vide. Ainsi, (^F) seul listerait également les fichiers normaux, par exemple. Ainsi, comme expliqué dans la page de manuel zshexp, nous avons également besoin du qualificatif (/) glob, qui répertorie uniquement les répertoires:

$ 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

Ainsi, pour vérifier si un répertoire donné est vide, vous pouvez donc lancer:

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

et juste pour être sûr qu'un répertoire non vide ne sera pas capturé:

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

Oops! Puisque Y n'est pas vide, zsh ne trouve aucune correspondance pour (/^F) ("les répertoires qui sont vides") et génère un message d'erreur indiquant qu'aucune correspondance pour le glob n'a été trouvée. Nous devons donc supprimer ces messages d'erreur possibles avec le qualificatif (N) glob:

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

Ainsi, pour les répertoires non vides, nous avons besoin du qualificatif (N/^F), que vous pouvez lire comme suit: "ne me prévenez pas des échecs, des répertoires qui ne sont pas pleins".

De même, pour les répertoires vides, nous avons besoin du qualificatif (NF), que nous pouvons également lire comme suit: "ne me prévenez pas des échecs, des répertoires complets".

1
Zorawar

Prenant un indice (ou plusieurs) de la réponse d’olibre, j’aime bien la fonction Bash:

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

Parce que même s’il crée un sous-shell, c’est aussi proche de la solution O(1) que je l’imagine et le nommer en le rendant lisible. Je peux alors écrire

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

En ce qui concerne O(1), il existe des cas exceptionnels: si un répertoire volumineux a été effacé de la totalité ou de la totalité sauf la dernière entrée, il peut être nécessaire de "lire" l'intégralité du texte pour déterminer s'il est vide. Je pense que les performances attendues sont O(1), mais dans le pire des cas, la taille du répertoire est linéaire. Je n'ai pas mesuré cela.

1
ForDummies

Petite variation de réponse de Bruno :

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

Ça marche pour moi

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

Je suis surpris que le guide wooledge sur les répertoires vides n'ait pas été mentionné. Ce guide, et tous les éléments de Wooledge, est incontournable pour les questions de type Shell.

À noter de cette page:

N'essayez jamais d'analyser la sortie de ls. Même les solutions ls -A peuvent casser (par exemple, sur HP-UX, si vous êtes root, ls -A le fait. quelque chose qui incroyablement stupide).

En fait, on peut souhaiter éviter complètement la question directe. Habituellement, les gens veulent savoir si un Le répertoire est vide car ils veulent faire quelque chose avec les fichiers qu’ils contiennent, etc. Regardez le plus gros question. Par exemple, l'un de ces exemples basés sur la recherche peut constituer une solution appropriée:

   # 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

Le plus souvent, tout ce dont vous avez vraiment besoin est quelque chose comme ceci:

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

En d'autres termes, la personne qui pose la question a peut-être pensé qu'un test de répertoire vide explicite était nécessaire pour éviter un message d’erreur tel que mympgviewer: ./*.mpg: Aucun fichier ou répertoire de ce type alors qu’en fait, aucun un tel test est requis.

0
bishop

J'irais pour find:

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

Ceci vérifie le résultat de la recherche de fichiers dans le répertoire, sans passer par les sous-répertoires. Ensuite, il vérifie la sortie à l'aide de l'option -z tirée de man test:

   -z STRING
          the length of STRING is zero

Voir quelques résultats:

$ mkdir aaa
$ dir="aaa"

Dir vide:

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

Juste dirs dedans:

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

Un fichier dans le répertoire:

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

Un fichier dans un sous-répertoire:

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

Avec quelques solutions de contournement, je pourrais trouver un moyen simple de savoir s'il existe des fichiers dans un répertoire. Cela peut s’étendre à d’autres avec les commandes grep pour vérifier spécifiquement les fichiers .xml ou .txt etc. Ex: 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

Jusqu'à présent, je n'ai pas vu de réponse utilisant grep qui, à mon avis, donnerait une réponse plus simple (avec pas trop de symboles étranges!). Voici comment je voudrais vérifier si des fichiers existent dans le répertoire avec Bourne Shell:

cela retourne le nombre de fichiers dans un répertoire:

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

vous pouvez renseigner le chemin du répertoire dans lequel répertoire est écrit. La première moitié du tube garantit que le premier caractère de la sortie est "-" pour chaque fichier. egrep compte ensuite le nombre de lignes commençant par ce symbole en utilisant des expressions régulières. tout ce que vous avez à faire est maintenant de stocker le nombre que vous avez obtenu et de le comparer à l’aide de citations arrières telles que: 

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

x est une variable de votre choix.

0
Jecht Tyre

Essayez avec la commande find . Spécifiez le répertoire codé en dur ou comme argument . Ensuite, lance find pour rechercher tous les fichiers du répertoire . Vérifiez si return of find est null . Écho des données de find

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

Réponse simple avec bash :

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

Mélanger des choses de pruneaux et les dernières réponses, je dois

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

cela fonctionne aussi pour les chemins avec des espaces

0
Laurent G