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 veuille savoir s'il existe des fichiers dont les noms correspondent au glob. Le glob peut correspondre à 0 fichier (auquel cas je n'ai 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 il serait préférable que je puisse le faire avec une seule if
instruction et sans boucles (simplement parce que je trouve cela plus lisible).
( test -e glob*
échoue si le glob correspond à plusieurs fichiers.)
Réponses:
Échappez au motif ou il sera pré-développé en matchs.
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.
MISE À JOUR : Exemple d'utilisation demandée.
la source
compgen
s'agit d'une commande intégrée spécifique à bash et qui ne fait pas partie des commandes intégrées spécifiées par le shell Unix POSIX standard. pubs.opengroup.org/onlinepubs/9699919799 pubs.opengroup.org/onlinepubs/9699919799/utilities/… Par conséquent, évitez de l'utiliser dans des scripts où la portabilité vers d'autres shells est une préoccupation.if ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fi
- peut-être utile pour le golf de code? Échoue s'il existe un fichier nommé le même que le glob, auquel le glob n'aurait pas dû correspondre, mais si c'est le cas, vous avez probablement de plus gros problèmes.if ls /tmp/*Files &> /dev/null; then echo exists; fi
compgen
, voirman bash
ou avechelp compgen
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 définirais qu'à l'intérieur du sous-shell qui étend le glob:
Pour une meilleure portabilité et une globalisation plus flexible, utilisez find:
Les actions explicites -print -quit sont utilisées pour rechercher au lieu de l'implicite par défaut action -print sorte que find se ferme dès qu'il trouve le premier fichier correspondant aux critères de recherche. Lorsque de nombreux fichiers correspondent, cela devrait fonctionner beaucoup plus rapidement que
echo glob*
ouls 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 petit, utilisez stat:
la source
find
semble être exactement correct. Il n'a pas de cas d'angle, puisque le shell ne fait pas d'expansion (et ne passe pas un glob non développé à une autre commande), il est portable entre les shells (bien qu'apparemment toutes les options que vous n'utilisez pas soient spécifiées par POSIX), et c'est plus rapide quels -d glob*
(la réponse acceptée précédente) car elle s'arrête lorsqu'elle atteint le premier match.shopt -u failglob
car ces options semblent en conflit d'une manière ou d'une autre.find
solution correspondra également à un nom de fichier sans caractères globaux. Dans ce cas, c'est ce que je voulais. Juste quelque chose à savoir cependant.-maxdepth
option pour une recherche POSIX.la source
nullglob
au lieu de vérifier si un seul résultat est égal au modèle lui-même. Certains modèles peuvent correspondre à des noms qui sont exactement égaux au modèle lui-même (par exemplea*b
, mais pas par exemplea?b
ou[a]
).touch '*py'
), mais cela m'indique une autre bonne direction."$M"
comme raccourci pour"${M[0]}"
. Sinon, vous avez déjà l'expansion glob dans une variable de tableau, vous êtes donc gtg pour la passer à d'autres choses sous forme de liste, au lieu de les faire redévelopper le glob.[
processus) avecif [[ $M ]]; then ...
J'aime
C'est à la fois lisible et efficace (sauf s'il y a un grand nombre de fichiers).
Le principal inconvénient est qu'il 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 trouvé comme n'existant pas non plus.Edit: il peut y avoir un faux positif, voir commentaire
la source
*.[cC]
(il peut y avoir pasc
ouC
fichier, mais un fichier appelé*.[cC]
) ou faux négatif si le premier fichier élargi de qui est par exemple un lien symbolique vers un fichier unexistent ou à un fichier dans un répertoire auquel vous n'avez pas accès (vous voulez en ajouter un|| [ -L "$1" ]
).-e
, lorsqu'il y a 0 ou 1 correspondance. Cela ne fonctionne pas pour plusieurs correspondances, car cela deviendrait[ -e file1 file2 ]
et cela échouerait. Voir également github.com/koalaman/shellcheck/wiki/SC2144 pour la justification et les solutions suggérées.Si vous avez défini globfail, vous pouvez utiliser ce fou (ce que vous ne devriez vraiment pas)
ou
la source
(shopt -s failglob; : *) 2>/dev/null && echo exists
test -e a la fâcheuse mise en garde qu'il considère que les liens symboliques rompus n'existent pas. Donc, vous voudrez peut-être aussi les vérifier.
la source
-o
et-a
danstest
/[
. Par exemple, ici, il échoue si$1
c'est=
avec la plupart des implémentations. Utilisez[ -e "$1" ] || [ -L "$1" ]
plutôt.Pour simplifier quelque peu la réponse de MYYN, basée sur son idée:
la source
[a]
un fichier nommé[a]
, mais aucun fichier nomméa
? J'aime toujoursnullglob
ça. Certains pourraient considérer cela comme pédant, mais nous pourrions tout aussi bien avoir raison que cela est raisonnable.[a]
doit seulement correspondrea
, pas le nom de fichier littéral[a]
.Sur la base de la réponse de flabdablet , pour moi, il semble que le plus simple (pas nécessairement le plus rapide) consiste à utiliser find lui-même, tout en laissant l'expansion de glob sur le shell, comme:
Ou
if
comme:la source
find
réponse de flabdablet car il accepte les chemins dans le glob et il est plus concis (ne nécessite pas-maxdepth
etc). Cela semble également meilleur que sastat
réponse, car il ne continue pas de faire desstat
efforts supplémentaires à chaque match de glob supplémentaire. J'apprécierais que quelqu'un puisse contribuer aux cas d'angle où cela ne fonctionne pas.-maxdepth 0
car cela permet plus de flexibilité dans l'ajout de conditions. par exemple, supposons que je souhaite restreindre le résultat aux fichiers correspondants uniquement. Je pourrais essayerfind $glob -type f -quit
, mais cela retournerait vrai si le glob ne correspondait pas à un fichier, mais correspondait à un répertoire qui contenait un fichier (même récursivement). En revanchefind $glob -maxdepth 0 -type f -quit
, ne retournerait vrai que si le glob lui-même correspondait à au moins un fichier. Notez que celamaxdepth
n'empêche pas le glob d'avoir un composant d'annuaire. (FYI2>
est suffisant. Pas besoin de&>
)find
en premier lieu est d'éviter que le shell génère et trie une liste potentiellement énorme de correspondances globales;find -name ... -quit
correspondra à au plus un nom de fichier. Si un script repose sur la transmission d'une liste de correspondances glob générées par le shell àfind
, l'invocationfind
n'entraîne rien d'autre qu'une surcharge de démarrage de processus inutile. Le simple fait de tester directement la liste résultante pour vérifier qu'elle n'est pas vide sera plus rapide et plus clair.J'ai encore une autre solution:
Cela fonctionne bien pour moi. Y a-t-il des cas d'angle qui me manquent?
la source
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:
Remarque: si vous avez
nullglob
défini,scripts
sera un tableau vide, et vous devriez tester avec[ "${scripts[*]}" ]
ou avec à la[ "${#scripts[*]}" != 0 ]
place. Si vous écrivez une bibliothèque qui doit fonctionner avec ou sansnullglob
, vous voudrezUn 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.
la source
if [ -e "${scripts[0]}" ]...
? Autorisez -vous également la possibilité de définir un ensemble de noms d' options de shell ?nounset
est actif. De plus, il peut être (légèrement) moins cher de tester que la chaîne n'est pas vide que de vérifier la présence d'un fichier. Peu probable cependant, étant donné que nous venons d'effectuer un glob, ce qui signifie que le contenu du répertoire devrait être frais dans le cache du système d'exploitation.Cette abomination semble fonctionner:
Cela nécessite probablement bash, pas sh.
Cela fonctionne car l'option nullglob oblige le glob à évaluer 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.
la source
if [ "`echo *py`" != "*py"]
*py
.py
,`echo *py`
sera évalué*py
.*py
, ce qui est un mauvais résultat.*py
, votre script fera écho à "Glob matched"?Je n'ai pas vu cette réponse, alors j'ai pensé l'avoir mise là:
la source
Explication
Lorsqu'il n'y a pas de correspondance pour
glob*
, alors$1
contiendra'glob*'
. Le test-f "$1"
ne sera pas vrai car leglob*
fichier n'existe pas.Pourquoi c'est mieux que des alternatives
Cela fonctionne avec sh et ses dérivés: ksh et bash. Il ne crée aucun sous-shell.
$(..)
et les`...`
commandes créent un sous-shell; ils bifurquent un processus, et sont donc plus lents que cette solution.la source
Comme ça dans frapper(fichiers de test contenant
pattern
):C'est bien mieux que
compgen -G
: parce que nous pouvons discriminer plus de cas et plus précisément.Peut fonctionner avec un seul caractère générique
*
la source
Notez que cela peut prendre beaucoup de temps s'il y a beaucoup de correspondances ou si l'accès aux fichiers est lent.
la source
[a]
est utilisé lorsque le fichier[a]
est présent et que le fichiera
est absent. Il dira «trouvé» même si le seul fichier auquel il doit correspondrea
, n'est pas réellement présent.la source
nullglob
option shell.nullglob
un kludge. La comparaison d'un seul résultat avec le modèle d'origine est un kludge (et sujette à de faux résultats), cenullglob
n'est pas le cas.nullglob
un kludge.la source
glob*
et, par exemple, vous n'avez pas l'écriture pour répertorier ces répertoires.ls | grep -q "glob.*"
Ce n'est 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 régulières sont plus puissantes que les modèles de glob bash ordinaires.
la source
la source