Tester si un glob a des correspondances dans bash

, Si je veux vérifier l'existence d'un seul fichier, je peux le tester à l'aide de test -e filename ou [ -e filename ].

Supposons que j'ai un glob et je veux savoir s'il existe des fichiers dont les noms correspondent au glob. Le glob peut correspondre à 0 fichiers (auquel cas je ne dois rien faire), ou il peut correspondre à 1 ou plusieurs fichiers (auquel cas je dois faire quelque chose). Comment puis-je tester si un glob a des correspondances? (Je me fiche du nombre de correspondances, et ce serait mieux si je pouvais le faire avec un if déclaration et pas de boucles (simplement parce que je trouve que le plus lisible).

(test -e glob* échoue si le glob correspond à plus d'un fichier.)

169
demandé sur Brian Chrisman 2010-05-30 07:22:46

18 réponses

Bash solution spécifique:

compgen -G "<glob-pattern>"

Échappez au motif ou il sera pré-développé en correspondances.

L'état de sortie est:

  • 1 pour non-match,
  • 0 pour 'une ou plusieurs correspondances'

stdout est une liste de fichiers correspondant au glob .
Je pense que c'est la meilleure option en termes de concision et de minimisation des effets secondaires potentiels.

UPDATE : exemple d'utilisation demandé.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi
105
répondu Brian Chrisman 2018-09-22 15:34:52

L'option shell nullglob est en effet un bashisme.

Pour éviter la nécessité d'une sauvegarde et d'une restauration fastidieuses de l'état nullglob, Je ne le mettrais que dans le sous-shell qui étend le glob:

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

Pour une meilleure portabilité et plus souple d'expansion, utilisez rechercher:

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

Explicite -print-quittez les actions sont utilisés pour trouver au lieu de la valeur par défaut implicite -print action, de sorte que trouver va quitter dès qu'il trouve le premier fichier correspondant aux critères de recherche. Lorsque beaucoup de fichiers correspondent, cela devrait fonctionner beaucoup plus rapidement que echo glob* ou ls glob* et cela évite également la possibilité de surcharger la ligne de commande étendue (certains shells ont une limite de longueur 4K).

SI find semble exagéré et que le nombre de fichiers susceptibles de correspondre est faible, utilisez stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
143
répondu flabdablet 2017-10-20 16:57:48
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi
20
répondu miku 2010-05-30 04:06:03

J'aime

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

C'est à la fois lisible et efficace (sauf s'il y a un grand nombre de fichiers).
Le principal inconvénient est que c'est beaucoup plus subtil qu'il n'y paraît, et je me sens parfois obligé d'ajouter un long commentaire.
S'il y a une correspondance, "glob*" est développé par le shell et toutes les correspondances sont passées à exists(), qui vérifie la première et ignore le reste.
S'il n'y a pas de correspondance, "glob*" est passé à exists() et n'existe pas non plus.

Edit: il peut y avoir un de faux positifs, voir commentaire

14
répondu Dan Bloch 2018-09-08 14:22:43

Test-e a la mise en garde malheureuse qu'il considère que les liens symboliques brisés n'existent pas. Donc, vous voudrez peut-être vérifier pour ceux, trop.

function globexists {
  test -e "$1" -o -L "$1"
}

if globexists glob*; then
    echo found
else
    echo not found
fi
6
répondu NerdMachine 2013-11-08 16:16:37

J'ai encore une autre solution:

if [ "$(echo glob*)" != 'glob*' ]

Cela fonctionne bien pour moi. Y at-il des cas de coin-je manqué?

4
répondu SaschaZorn 2015-11-06 14:39:30

Basé sur la réponse de flabdablet , pour moi, il semble que le plus facile (pas nécessairement le plus rapide) est juste d'utiliser find lui-même, tout en laissant l'expansion glob sur shell, comme:

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

Ou dans if comme:

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi
3
répondu queria 2017-05-23 11:54:58

Si vous avez globfail défini, vous pouvez utiliser ce fou (que vous ne devriez vraiment pas)

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

Ou

q=( * ) && echo 0 || echo 1
3
répondu Prospero 2014-06-28 14:04:35

Pour simplifier quelque peu la réponse de MYYN, basée sur son idée:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi
2
répondu Ken Bloom 2010-05-30 04:07:42

Cette abomination semble fonctionner:

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

Il nécessite probablement bash, pas sh.

Cela fonctionne parce que l'option nullglob provoque l'évaluation de glob à une chaîne vide s'il n'y a pas de correspondance. Ainsi, toute sortie non vide de la commande echo indique que le glob correspond à quelque chose.

1
répondu Ryan Thompson 2014-02-24 07:27:52

Je n'ai pas vu cette réponse, alors j'ai pensé que je la mettrais là-bas:

set -- glob*
[ -f "$1" ] && echo "found $@"
1
répondu Brad Howes 2017-07-27 19:46:58
(ls glob* &>/dev/null && echo Files found) || echo No file found
0
répondu Damodharan R 2010-05-30 03:37:48
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

Notez que cela peut être très long s'il y a beaucoup de correspondances ou si l'accès aux fichiers est lent.

0
répondu Florian Diesch 2010-05-30 03:43:42
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi
0
répondu Peter Lyons 2010-05-30 17:01:57

[ ls glob* 2>/dev/null | head -n 1 ] && echo vrai

0
répondu otocan 2017-07-27 12:03:03

Dans Bash, vous pouvez glob à un tableau; si le glob ne correspond pas, votre tableau contiendra une seule entrée qui ne correspond pas à un fichier existant:

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

Remarque: si vous avez nullglob set, scripts sera un tableau vide, et vous devez tester avec [ "${scripts[*]}" ] ou de [ "${#scripts[*]}" != 0 ] à la place. Si vous écrivez une bibliothèque qui doit fonctionner avec ou sans nullglob, vous voudrez

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

Un avantage de cette approche est que vous avez alors la liste des fichiers avec lesquels vous voulez travailler, plutôt que d'avoir à répétez l'opération glob.

0
répondu Toby Speight 2018-05-03 11:23:34

Vous pouvez faire ce qui suit:

Ensemble -- glob* if [ -f "$1" ]; then printf " BLAH" fi

Cela fonctionne avec sh et dérivés: ksh et bash. Il ne crée aucun sous-shell. $(..)et les commandes ... créent un sous-shell: elles forkent un processus, et elles sont inefficaces. Bien sûr, cela fonctionne avec plusieurs fichiers, et cette solution peut être la plus rapide, ou la deuxième à la plus rapide.

Quand il n'y a pas de correspondance, FPR glob* $1 contiendra 'glob*'. Le test-F "$1 " ne sera pas vrai parce que le fichier glob* n'existe pas. C'est ok pour tous les cas

0
répondu joseyluis 2018-07-18 12:24:35

ls | grep -q "glob.*"

Pas la solution la plus efficace (s'il y a une tonne de fichiers dans le répertoire, cela peut être lent), mais c'est simple, facile à lire et a également l'avantage que les expressions rationnelles sont plus puissantes que les modèles bash glob.

-1
répondu jesjimher 2016-04-08 10:58:14