Commande Bash pour voir si des fichiers dans dir – testent si un répertoire est vide

J’ai le script bash suivant:

if ls /Users/david/Desktop/empty > /dev/null then echo 'yes -- files' else echo 'no -- files' fi 

Comment pourrais-je modifier la ligne supérieure de sorte qu’elle soit vraie s’il y a un ou plusieurs fichiers dans le répertoire /Users/david/Desktop/empty ?

Ceci est couvert en détail dans BashFAQ # 004 . Notamment, l’ utilisation de ls à cette fin est un anti-comportement et devrait être évitée .

 shopt -s dotglob # if including hidden files is desired files=( "$dir"/* ) [[ -e $files || -L $files ]] && echo "Directory is not empty" 

[[ -e $files ]] ne vérifie pas réellement si le contenu du tableau entier existe; plutôt, il vérifie le premier nom renvoyé – qui gère le cas où aucun fichier ne correspond, dans lequel l’expression glob elle-même est renvoyée comme résultat unique.


Notamment:

  • C’est beaucoup plus rapide que d’invoquer ls , qui nécessite d’utiliser fork() pour générer un sous-shell, execve() pour remplacer ce sous-shell par /bin/ls , l’éditeur de liens dynamic du système d’exploitation pour charger les bibliothèques partagées par le binary ls , etc. . [Une exception à cette règle concerne les répertoires extrêmement volumineux, composés de dizaines de milliers de fichiers – un cas dans lequel ls sera également lent; voir la solution basée sur la find ci-dessous pour ceux].
  • C’est plus correct que d’invoquer ls : la liste des fichiers renvoyés par la mise en forme est garantie pour correspondre exactement aux noms littéraux des fichiers, alors que ls peut générer des noms avec des caractères cachés. Si la première entrée est un nom de fichier valide, "${files[@]}" peut être parcouru en toute sécurité avec l’assurance que chaque valeur renvoyée sera un nom, et il n’ya pas lieu de s’inquiéter des systèmes de fichiers avec des nouvelles lignes compter si l’implémentation locale ls ne leur échappe pas.

Cela dit, une autre approche consiste à utiliser find , si vous en avez une avec l’extension -empty (disponible à la fois à partir de GNU find et des BSD modernes, y compris Mac OS):

 [[ $(find -H "$dir" -maxdepth 0 -type d -empty) ]] || echo "Directory is not empty" 

… si un résultat est donné, le répertoire est vide. Bien que plus lent que les répertoires qui ne sont pas inhabituellement volumineux, cela est plus rapide que ls ou le globbing pour les répertoires extrêmement volumineux non présents dans le cache direntry, car il peut renvoyer des résultats sans parsing complète.

Des solutions robustes Bash :

Pour savoir pourquoi une solution Bash pure avec globbing est supérieure à l’utilisation de ls , consultez la réponse utile de Charles Duffy , qui contient également une alternative basée sur la find , qui est beaucoup plus rapide et nécessite moins de mémoire avec les grands répertoires . [1]
Considérons également la réponse à base de stat , aussi rapide et efficace sur la mémoire, d’Anubhava , qui nécessite cependant des formes de syntaxe distinctes sous Linux et BSD / OSX.

Mise à jour pour une solution plus simple, adaptée avec reconnaissance de cette réponse .

 # EXCLUDING hidden files and folders - note the *quoted* use of glob '*' if compgen -G '*' >/dev/null; then echo 'not empty' else echo 'empty, but may have hidden files/dirs.' fi 
  • compgen -G est normalement utilisé pour l’achèvement de tabulation, mais il est utile dans ce cas également:

    • Notez que compgen -G effectue ses propres opérations de globalisation, vous devez donc lui passer le glob (modèle de nom de fichier) entre guillemets pour qu’il affiche toutes les correspondances. Dans ce cas particulier, même le fait de passer un modèle non coté à l’avance fonctionnerait, mais la différence ne vaut rien.

    • si rien ne correspond, compgen -G ne produit toujours aucune sortie (quel que soit l’état de l’option nullglob ), et indique par son code de sortie si au moins 1 correspondance a été trouvée, ce que le conditionnel tire (en supprimant toute sortie standard) sortie avec >/dev/null ).

 # INCLUDING hidden files and folders - note the *unquoted* use of glob * if (shopt -s dotglob; compgen -G * >/dev/null); then echo 'not empty' else echo 'completely empty' fi 
  • compgen -G ne correspond jamais aux éléments masqués (quel que soit l’état de l’option dotglob ), une solution de contournement est donc nécessaire pour trouver les éléments masqués:

    • (...) crée un sous-shell pour le conditionnel; c’est-à-dire que les commandes exécutées dans le sous-shell n’affectent pas l’environnement du shell actuel, ce qui nous permet de définir l’option dotglob de manière localisée.

    • shopt -s dotglob fait correspondre * éléments cachés (sauf pour . et .. ).

    • compgen -G * avec un non compgen -G * * , grâce à l’expansion compgen -G * du shell, est soit passé au moins un nom de fichier, masqué ou non (les noms de fichiers supplémentaires sont ignorés) soit la chaîne vide, si aucun élément caché ou non n’existe . Dans le premier cas, le code de sortie est 0 (succès de la signalisation et donc un répertoire non vide), dans le dernier 1 (signalant un répertoire vraiment vide).


[1] Cette réponse prétendait à l’origine offrir une solution Bash uniquement efficace avec les grands répertoires, basée sur l’approche suivante: (shopt -s nullglob dotglob; for f in "$dir"/*; do exit 0; done; exit 1) . Ceci n’est PAS plus efficace car, en interne, Bash collecte d’abord toutes les correspondances dans un tableau avant d’entrer dans la boucle – en d’autres termes: for * n’est pas évalué paresseusement.

Voici une solution basée sur la commande stat qui peut renvoyer le nombre de liens matériels s’il est exécuté sur un répertoire (ou un lien vers un répertoire). Il commence à incrémenter le nombre de liens durs à partir de 3 car les deux premiers sont . et .. entrées soustrayant ainsi 2 de ce nombre donne comme nombre réel d’entrées dans le répertoire donné (cela inclut également les liens symboliques).

Donc, mettre tout cela ensemble:

 (( ($(stat -Lc '%h' "$dir") - 2) > 0)) && echo 'not empty' || echo 'empty' 

Selon l’ man stat options de man stat utilisées sont les suivantes:

 %h number of hard links -L --dereference, follow links 

EDIT: Pour le rendre compatible avec BSD / OSX:

 (( ($(stat -Lf '%l' "$dir") - 2) > 0)) && echo 'not empty' || echo 'empty'