Vérifier si un fichier existe avec un joker dans le script shell [dupliquer]

cette question a déjà une réponse ici:

  • Tester si un glob a toutes les correspondances dans bash 18 réponses

j'essaie de vérifier si un fichier existe, mais avec un joker. Voici mon exemple:

if [ -f "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

j'ai aussi essayé sans le double citation.

229
demandé sur tripleee 2011-06-15 23:50:26

21 réponses

le plus simple devrait être de se fier à la valeur de retour ls (il retourne non-zéro lorsque les fichiers n'existent pas):

if ls /path/to/your/files* 1> /dev/null 2>&1; then
    echo "files do exist"
else
    echo "files do not exist"
fi

j'ai redirigé la sortie ls pour la rendre complètement silencieuse.


EDIT: depuis cette réponse a obtenu un peu d'attention (et des remarques critiques très utiles comme des commentaires), voici une optimisation qui repose également sur l'expansion glob, mais évite l'utilisation de ls :

for f in /path/to/your/files*; do

    ## Check if the glob gets expanded to existing files.
    ## If not, f here will be exactly the pattern above
    ## and the exists test will evaluate to false.
    [ -e "$f" ] && echo "files do exist" || echo "files do not exist"

    ## This is all we needed to know, so we can break after the first iteration
    break
done

c'est très similaire à la réponse de @grok12, mais cela évite l'itération inutile à travers toute la liste.

336
répondu Costi Ciudatu 2014-11-14 16:35:48

si votre shell a une option nullglob et qu'il est activé, un motif de Joker qui ne correspond à aucun fichier sera supprimé de la ligne de commande. Cela fera ls ne voir aucun argument pathname, liste le contenu du répertoire courant et réussir, ce qui est faux. GNU stat , qui échoue toujours si on ne lui donne aucun argument ou un argument nommant un fichier inexistant, serait plus robuste. Aussi, la redirection l'opérateur est un bashisme .

if stat --printf='' /path/to/your/files* 2>/dev/null
then
    echo found
else
    echo not found
fi

mieux encore est GNU find , qui peut gérer une recherche de jokers en interne et sortir dès qu'il trouve un fichier correspondant, plutôt que de perdre du temps à traiter une liste potentiellement énorme d'entre eux étendue par le shell; cela évite également le risque que le shell puisse déborder son tampon de ligne de commande.

if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)"
then
    echo found
else
    echo not found
fi

non-GNU versions de find pourrait ne pas avoir l'option - maxdepth utilisée ici pour faire trouver recherche seulement le /dir/to/recherche au lieu de l'arbre de répertoire entier enraciné là.

48
répondu flabdablet 2011-10-09 08:33:41

voici ma réponse -

files=(xorg-x11-fonts*)

if [ -e "${files[0]}" ];
then
    printf "BLAH"
fi
36
répondu Pankaj Parashar 2013-07-02 09:35:13
for i in xorg-x11-fonts*; do
  if [ -f "$i" ]; then printf "BLAH"; fi
done

cela fonctionnera avec plusieurs fichiers et avec de l'espace blanc dans les noms de fichiers.

18
répondu grok12 2016-05-27 18:19:11

mise à jour:

Ok, maintenant j'ai définitivement la solution:

files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
   echo "Exists"
else
    echo "None found."
fi

> Exists
13
répondu Swift 2011-06-15 20:30:57

peut-être que cela aidera quelqu'un:

if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi
12
répondu Marian 2014-01-07 16:54:06

vous pouvez faire ce qui suit:

set -- xorg-x11-fonts*
if [ -f "" ]; then
    printf "BLAH"
fi

cela fonctionne avec sh et ses dérivés: KSH et bash. Il ne crée pas de sous-shell. $(..)et.` .. les commandes ' créent un sous-shell : elles bifurquent un processus et sont inefficaces. Bien sûr qu'il fonctionne avec plusieurs fichiers, et cette solution peut être le plus rapide ou le plus rapide.

ça marche aussi quand il n'y a pas d'allumettes. Il n'est pas nécessaire d'utiliser nulllglob comme le dit l'un des commentateurs. 1 $ contiendra le nom original du test, donc le test-F $1 ne réussira pas, parce que le fichier $1 n'existe pas.

12
répondu joseyluis 2018-09-03 08:07:05

la question n'était pas spécifique à Linux / Bash donc j'ai pensé que je voudrais ajouter la façon Powershell-qui traite les caractères génériques différent - vous l'avez mis dans les citations comme ci-dessous:

If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){
  Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf
  Remove-Item -force -v -path ./output/test-pdf-docx/*.docx
}

je pense que c'est utile parce que le concept de la question originale couvre les" shells " en général pas seulement Bash ou Linux, et s'appliquerait aux utilisateurs de Powershell avec la même question aussi.

5
répondu Jeremy Hajek 2015-09-13 02:41:47

strictement parlant, si vous voulez seulement imprimer "Blah" voici la solution:

find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit

Voici une autre façon:

doesFirstFileExist(){
    test -e ""
}

if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi

mais je pense que le plus optimal est comme suit, parce qu'il ne va pas essayer de trier les noms de fichiers:

if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ]
then printf "BLAH"
fi
4
répondu Vouze 2016-10-26 13:04:19

le code bash que j'utilise

if ls /syslog/*.log > /dev/null 2>&1; then 
   echo "Log files are present in /syslog/; 
fi

Merci!

3
répondu CapitanBlack 2016-04-12 18:34:47

Voici une solution pour votre problème spécifique qui ne nécessite pas de boucles for ou des commandes externes comme ls , find et autres.

if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then
    printf "BLAH"
fi

comme vous pouvez le voir, c'est juste un peu plus compliqué que ce que vous espériez, et s'appuie sur le fait que si le shell n'est pas capable d'étendre la globalité, cela signifie qu'aucun fichier avec cette globalité n'existe et echo produira la globalité comme est , qui nous permet de faire une simple comparaison de chaînes pour vérifier si l'une de ces fichiers existent.

si nous devions généraliser la procédure , cependant, nous devrions tenir compte du fait que les fichiers pourraient contenir des espaces dans leurs noms et/ou chemins et que le glob char pourrait légitimement s'étendre à rien (dans votre exemple, ce serait le cas d'un fichier dont le nom est exactement xorg-x11-fonts).

Cela pourrait être réalisé par la fonction suivante, dans bash .

function doesAnyFileExist {
   local arg="$*"
   local files=($arg)
   [ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ]
}

pour revenir à votre exemple, il pourrait être invoqué comme ceci.

if doesAnyFileExist "xorg-x11-fonts*"; then
    printf "BLAH"
fi

expansion Glob devrait se produire dans la fonction elle-même pour qu'il fonctionne correctement, c'est pourquoi je mets l'argument entre guillemets et c'est ce que la première ligne dans le corps de fonction est là pour: de sorte que n'importe quel les arguments multiples (qui pourraient être le résultat d'une expansion glob en dehors de la fonction, ainsi qu'un paramètre fictif) seraient fusionnés en un seul. Une autre approche pourrait être de soulever une erreur s'il y a plus d'un argument, encore un autre pourrait être d'ignorer tout sauf le premier argument.

la deuxième ligne dans le corps de fonction définit le files var à un tableau constitué par tous les noms de fichiers que le glob étendu à, un pour chaque tableau élément. c'est très bien si les noms de fichier contiennent des espaces , chaque élément de tableau contiendra les noms comme , y compris les espaces.

la troisième ligne dans le corps de fonction fait deux choses:

  1. Il vérifie d'abord si il y a plus d'un élément dans le tableau. Si c'est le cas, cela signifie que le "glob sûrement a été élargi à quelque chose (en raison de ce que nous avons fait sur le 1st line), ce qui implique qu'il existe au moins un fichier correspondant au glob, ce que nous voulions savoir.

  2. si à l'étape 1. nous avons découvert que nous avons moins de 2 éléments dans le tableau, puis nous vérifions si nous en avons un et si c'est le cas, nous vérifions si celui-ci existe, de la manière habituelle. Nous avons besoin de faire cette vérification supplémentaire afin de tenir compte des arguments de fonction sans amarres, dans ce cas le tableau contient seulement un, unpanded , élément.

2
répondu Fabio A. 2016-11-25 15:32:58

j'utilise ceci:

filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`  
if [ $filescount -gt 0 ]; then  
    blah  
fi
1
répondu Alexandre Vr. 2014-10-21 18:59:55

IMHO il est préférable d'utiliser find toujours lors des tests pour les fichiers, globs ou répertoires. La pierre d'achoppement pour ce faire est le statut de sortie de find : 0 si tous les chemins ont été traversés avec succès, >0 autrement. L'expression que vous avez passée à find ne crée pas d'écho dans son code de sortie.

L'exemple suivant teste si un répertoire contient des entrées:

$ mkdir A
$ touch A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty'
not empty

quand A n'a pas de fichiers grep échoue:

$ rm A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty'
empty

Quand A n'existe pas grep échoue à nouveau parce que find imprime uniquement sur la sortie stderr:

$ rmdir A
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' || echo 'empty'
find: 'A': No such file or directory
empty

remplacer -not -empty par toute autre expression find , mais attention si vous -exec une commande qui imprime à stdout. Vous pouvez grep pour une expression spécifique dans de tels cas.

cette approche fonctionne bien dans les scripts shell. La question initiale était de cherchez le glob xorg-x11-fonts* :

if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q .
then
    : the glob matched
else
    : ...not
fi

notez que la branche else est atteinte si xorg-x11-fonts* n'a pas été apparié, ou find rencontré une erreur. Pour distinguer le cas utiliser $? .

1
répondu Andreas Spindler 2016-03-20 11:37:30
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
1
répondu Dan Elias 2017-10-02 03:09:53

Essayez cette

fileTarget="xorg-x11-fonts*"

filesFound=$(ls $fileTarget)  # 2014-04-03 edit 2: removed dbl-qts around $(...)

modifier 2014-04-03 (supprimé dbl-citations et ajouté le fichier de test " Charlie 22.html' (2 espaces)

case ${filesFound} in
  "" ) printf "NO files found for target=${fileTarget}\n" ;;
   * ) printf "FileTarget Files found=${filesFound}\n" ;;
esac 

Test

fileTarget="*.html"  # where I have some html docs in the current dir

FileTarget Files found=Baby21.html
baby22.html
charlie  22.html
charlie21.html
charlie22.html
charlie23.html

fileTarget="xorg-x11-fonts*"

NO files found for target=xorg-x11-fonts*

Notez que cela ne fonctionne que dans le répertoire courant, ou lorsque le var fileTarget comprend le chemin que vous voulez examiner.

0
répondu shellter 2014-04-03 03:27:07

Que Diriez-vous de

if ls -l  | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob
then
     # do something
else
     # do something else
fi 
0
répondu abc 2017-06-23 02:08:13

S'il y a une énorme quantité de fichiers sur un dossier réseau en utilisant le Joker est discutable (vitesse, ou dépassement d'arguments en ligne de commande).

j'ai fini par:

if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
  echo Such file exists
fi
0
répondu luxigo 2017-09-02 08:00:34

vous pouvez également supprimer d'autres fichiers

if [ -e $( echo  | cut -d" " -f1 ) ] ; then
   ...
fi
0
répondu McCloud 2017-10-10 20:43:38

utilisant les nouvelles fonctionnalités de fantaisie shmancy dans les shells ksh, bash et zsh (cet exemple ne gère pas les espaces dans les noms de fichiers):

# Declare a regular array (-A will declare an associative array. Kewl!)
declare -a myarray=( /mydir/tmp*.txt )
array_length=${#myarray[@]}

# Not found if the 1st element of the array is the unexpanded string
# (ie, if it contains a "*")
if [[ ${myarray[0]} =~ [*] ]] ; then
   echo "No files not found"
elif [ $array_length -eq 1 ] ; then
   echo "File was found"
else
   echo "Files were found"
fi

for myfile in ${myarray[@]}
do
  echo "$myfile"
done

oui, ça sent le Perl. Je suis content de ne pas intervenir ;)

0
répondu Ben Slade 2018-02-22 16:12:55

a trouvé quelques solutions intéressantes à partager. Le premier souffre toujours de "cela se cassera s'il y a trop de matchs" problème:

pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"

(rappelez-vous que si vous utilisez un tableau sans la syntaxe [ ] , vous obtenez le premier élément du tableau.)

si vous avez "shopt-s nullglob" dans votre script, vous pouvez simplement faire:

matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"

Maintenant, si c'est possible d'avoir une tonne de fichiers dans un répertoire, vous êtes assez bien coincé avec l'utilisation de find:

find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'
0
répondu Chris Cogdon 2018-03-09 21:43:22

essai man

if [ -e file ]; then
...  
fi

fonctionnera pour dir\file.

concerne

-14
répondu Shokodemon 2013-04-16 22:02:23