J'essaie de vérifier si un fichier existe, mais avec un caractère générique. Voici mon exemple:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
J'ai également essayé sans les guillemets.
Le plus simple devrait être de s'appuyer sur la valeur de retour ls
(elle renvoie une valeur différente de 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: Comme cette réponse a suscité un peu d’attention (et des remarques critiques très utiles sous forme de commentaires), voici une optimisation qui repose également sur une expansion globale, 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
Ceci est très similaire à la réponse de @ grok12, mais évite les itérations inutiles à travers toute la liste.
Si votre Shell dispose d'une option nullglob et qu'il est activé, un modèle de caractère générique qui ne correspond à aucun fichier ne sera totalement supprimé de la ligne de commande. Cela fera ls ne voir aucun argument de chemin d'accès, lister le contenu du répertoire courant et réussir, ce qui est faux. GNU _ stat , qui échoue toujours si aucun argument n'est attribué ou si un argument nomme un fichier inexistant, serait plus robuste. De plus, l'opérateur de redirection &> est un bashism.
if stat --printf='' /path/to/your/files* 2>/dev/null
then
echo found
else
echo not found
fi
Mieux vaut toujours GNU find , qui peut gérer une recherche générique en interne et se terminer dès qu'il trouve un fichier correspondant, plutôt que perdre du temps à traiter une liste potentiellement très longue d’entre eux développée par Shell; cela évite également le risque que le shell ne déborde de sa mémoire 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
Les versions non-GNU de find peuvent ne pas avoir l'option - maxdepth utilisée ici pour faire find rechercher uniquement le /dir/to/search à la place de toute l'arborescence de répertoires qui y est enracinée.
Voici ma réponse -
files=(xorg-x11-fonts*)
if [ -e "${files[0]}" ];
then
printf "BLAH"
fi
Vous pouvez faire ce qui suit:
set -- xorg-x11-fonts*
if [ -f "$1" ]; then
printf "BLAH"
fi
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: elles lancent un processus et sont inefficaces. Bien sûr, cela fonctionne avec plusieurs fichiers, et cette solution peut être la plus rapide, ou la seconde après la plus rapide.
Cela fonctionne aussi quand il n'y a pas d'allumettes. Il n'est pas nécessaire d'utiliser nullglob comme le dit l'un des commentateurs. $ 1 contiendra le nom du test d'origine, donc le test -f $ 1 ne réussira pas, car le fichier $ 1 n'existe pas.
for i in xorg-x11-fonts*; do
if [ -f "$i" ]; then printf "BLAH"; fi
done
Cela fonctionnera avec plusieurs fichiers et avec un espace dans les noms de fichiers.
PDATE:
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
Peut-être que cela aidera quelqu'un:
if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
La question n'étant pas spécifique à Linux/Bash, j'ai donc pensé ajouter la méthode Powershell - qui traite les caractères génériques de manière différente - vous le mettez en 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 cela est utile car le concept de la question initiale englobe les "shells" en général, pas seulement Bash ou Linux, et s’appliquerait également aux utilisateurs de Powershell.
À proprement parler, si vous voulez seulement imprimer "Blah", voici la solution:
find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit
Voici un autre moyen:
doesFirstFileExist(){
test -e "$1"
}
if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi
Mais je pense que le plus optimal est le suivant, car il ne tentera pas de trier les noms de fichiers:
if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ]
then printf "BLAH"
fi
Le code bash que j'utilise
if ls /syslog/*.log > /dev/null 2>&1; then
echo "Log files are present in /syslog/;
fi
Merci!
Voici une solution pour votre spécifique problème qui ne nécessite pas de boucles for
ou de commandes externes telles que ls
, find
, etc.
if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
Comme vous pouvez le constater, c’est un peu plus compliqué que ce que vous espériez et repose sur le fait que si le shell n’est pas en mesure d’agrandir le glob, cela signifie qu’il n’existe aucun fichier avec ce glob et que echo
le sera affiche le glob tel quel , ce qui nous permet de faire une simple comparaison de chaîne pour vérifier si l'un de ces fichiers existe.
Si nous devions généraliser la procédure, cependant, nous devrions prendre en compte le fait que les fichiers peuvent contenir des espaces dans leurs noms et/ou leurs chemins et que le caractère global pourrait à juste titre développez à 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
L’expansion globale doit se produire au sein de la fonction elle-même pour que celle-ci fonctionne correctement, c’est pourquoi je mets l’argument entre guillemets et c’est la raison pour laquelle la première ligne du corps de la fonction existe: afin que tous les arguments (qui pourraient expansion en dehors de la fonction, ainsi qu’un paramètre parasite) seraient fusionnés en un seul. Une autre approche pourrait consister à générer une erreur s'il y a plus d'un argument, mais une autre solution consiste à ignorer tout sauf le premier argument.
La deuxième ligne du corps de la fonction définit la variable files
var en un tablea constitué de tous les noms de fichiers développés par le glob, un pour chaque élément du tableau. C'est bien si les noms de fichiers contiennent des espaces, chaque élément du tableau contiendra les noms tels quels , y compris les espaces.
La troisième ligne du corps de la fonction fait deux choses:
Il vérifie d'abord s'il y a plus d'un élément dans le tableau. Si tel est le cas, cela signifie que le glob sûrement a été étendu à quelque chose (à cause de ce que nous avons fait sur la 1ère ligne), ce qui implique qu’au moins un fichier correspondant au glob existe, ce qui est tout ce que nous voulions à savoir.
Si à l'étape 1. nous avons découvert que nous avions moins de 2 éléments dans le tableau, nous vérifions si nous en avions un et si oui, nous vérifions si celui-ci existe, de la manière habituelle. Nous devons faire cette vérification supplémentaire afin de prendre en compte les arguments de la fonction sans glob chars, auquel cas le tableau ne contient qu'un seul élément, non développé.
J'utilise ceci:
filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`
if [ $filescount -gt 0 ]; then
blah
fi
IMHO, il est préférable d'utiliser find
toujours lors du test de fichiers, de fichiers globaux ou de répertoires. La pierre d'achoppement est alors l'état de sortie de find
: 0 si tous les chemins ont été parcourus avec succès,> 0 sinon. L'expression que vous avez transmise à find
ne crée pas d'écho dans son code de sortie.
L'exemple suivant teste si un répertoire a des entrées:
$ mkdir A
$ touch A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty'
not empty
Lorsque A
n'a pas de fichier grep
échoue:
$ rm A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty'
empty
Lorsque A
n'existe pas grep
échoue à nouveau car find
s'imprime uniquement sur 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
Remplacez -not -empty
par une autre expression find
, mais soyez prudent si vous -exec
une commande qui affiche sur la sortie standard. Vous voudrez peut-être rechercher une expression plus spécifique dans de tels cas.
Cette approche fonctionne bien dans les scripts Shell. La question à l'origine était de rechercher 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*
ne correspondait pas ou si find
rencontrait une erreur. Pour distinguer le cas, utilisez $?
.
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
Vous pouvez également couper d'autres fichiers
if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then
...
fi
Que diriez-vous
if ls -l | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a Shell glob
then
# do something
else
# do something else
fi
Utilisation de nouvelles fonctionnalités fantaisistes dans les shsh 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. Heureux de ne pas être intervenu;)
Essaye ça
fileTarget="xorg-x11-fonts*"
filesFound=$(ls $fileTarget) # 2014-04-03 edit 2: removed dbl-qts around $(...)
edit 2014-04-03 (suppression des citations de liens et ajout du fichier 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 actuel ou lorsque varfileTarget
inclut le chemin que vous voulez inspecter.
Nous avons trouvé deux solutions intéressantes à partager. Le premier souffre toujours du problème "cela va casser s'il y a trop de correspondances":
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, s’il est possible d’avoir une tonne de fichiers dans un répertoire, vous êtes plutôt coincé avec find:
find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'
S'il existe une grande quantité de fichiers sur un dossier réseau, l'utilisation du caractère générique est discutable (débordement d'arguments en vitesse ou en ligne de commande).
J'ai fini avec:
if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
echo Such file exists
fi