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.)
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
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
#!/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
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
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
J'ai encore une autre solution:
if [ "$(echo glob*)" != 'glob*' ]
Cela fonctionne bien pour moi. Y at-il des cas de coin-je manqué?
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
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
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
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.
Je n'ai pas vu cette réponse, alors j'ai pensé que je la mettrais là-bas:
set -- glob*
[ -f "$1" ] && echo "found $@"
(ls glob* &>/dev/null && echo Files found) || echo No file found
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.
#!/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
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.
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
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.