Si je veux vérifier l'existence d'un seul fichier, je peux le tester en utilisant test -e filename
ou [ -e filename ]
.
Supposons que j'ai un glob et que je veux savoir s'il existe des fichiers dont le nom correspond au glob. Le glob peut correspondre à 0 fichiers (auquel cas je n'ai rien à faire), ou à 1 ou plusieurs fichiers (dans ce cas, je dois faire quelque chose). Comment puis-je tester si un glob a des correspondances? (Je me moque du nombre de correspondances et il serait préférable de pouvoir le faire avec une déclaration if
et aucune boucle (tout simplement parce que je trouve cela très lisible).
(test -e glob*
échoue si le glob correspond à plus d'un fichier.)
Bash solution spécifique:
compgen -G "<glob-pattern>"
Echappez au modèle ou il sera pré-développé en allumettes.
Le statut de sortie est:
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ée.
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 fastidieuse et de la restauration de l'état nullglob, je ne l'aurais placé 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 une navigation plus flexible, utilisez find:
if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
echo found
else
echo not found
fi
Les actions -print -quit explicites sont utilisées pour find au lieu de l'action implicite -print de sorte que find se ferme dès qu'il trouve le premier fichier correspondant à la recherche Critères. Lorsque beaucoup de fichiers correspondent, cela devrait être beaucoup plus rapide que echo glob*
ou ls glob*
et éviter la possibilité de surcharger la ligne de commande étendue (certains shells ont une limite de longueur de 4 Ko).
Si find a l’impression d’être un peu 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 existe un très 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 existe 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'est pas non plus présent.
Edit: il peut y avoir un faux positif, 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-là aussi.
function globexists {
test -e "$1" -o -L "$1"
}
if globexists glob*; then
echo found
else
echo not found
fi
Si vous avez globfail réglé, vous pouvez utiliser ce fou (ce que vous ne devriez vraiment pas faire)
shopt -s failglob # exit if * does not match
( : * ) && echo 0 || echo 1
ou
q=( * ) && echo 0 || echo 1
J'ai encore une autre solution:
if [ "$(echo glob*)" != 'glob*' ]
Cela fonctionne bien pour moi. Y a-t-il des cas de coin qui me manquent?
D'après la réponse de flabdablet , il me semble que le plus simple (pas nécessairement le plus rapide) consiste simplement à utiliser find lui-même, tout en laissant l'expansion globale 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
Pour simplifier quelque peu la réponse du 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
Cela nécessite probablement bash, pas sh.
Cela fonctionne parce que l'option nullglob amène le glob à se transformer en 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.
Dans Bash, vous pouvez effectuer une sélection dans un tableau. si le glob ne correspond pas, votre tableau contiendra une seule entrée qui ne correspond à aucun fichier existant:
#!/bin/bash
shellglob='*.sh'
scripts=($shellglob)
if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi
Remarque: si vous avez défini nullglob
, scripts
sera un tableau vide, et vous devez tester avec [ "${scripts[*]}" ]
ou avec [ "${#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 disposez alors de la liste des fichiers avec lesquels vous souhaitez travailler, plutôt que de devoir répéter l'opération glob.
Je n'ai pas vu cette réponse, alors j'ai pensé la publier:
set -- glob*
[ -f "$1" ] && echo "found $@"
#!/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
if ls -d $glob > /dev/null 2>&1; then
echo Found.
else
echo Not found.
fi
Notez que cela peut prendre beaucoup de temps s'il y a beaucoup de correspondances ou que l'accès aux fichiers est lent.
[ls glob* 2>/dev/null | head -n 1
] && echo true
set -- glob*
if [ -f "$1" ]; then
echo "It matched"
fi
Lorsqu'il n'y a pas de correspondance pour glob*
, alors $1
contiendra 'glob*'
. Le test -f "$1"
ne sera pas vrai car le fichier glob*
n'existe pas.
Cela fonctionne avec sh et dérivés: ksh et bash. Cela ne crée pas de sous-shell. Les commandes $(..)
et `...`
créent un sous-shell; ils lancent un processus et sont donc plus lents que cette solution.