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.
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.
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à.
voici ma réponse -
files=(xorg-x11-fonts*)
if [ -e "${files[0]}" ];
then
printf "BLAH"
fi
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.
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
peut-être que cela aidera quelqu'un:
if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
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.
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.
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
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 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:
-
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.
-
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.
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 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 $?
.
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
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.
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
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
vous pouvez également supprimer d'autres fichiers
if [ -e $( echo | cut -d" " -f1 ) ] ; then
...
fi
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 ;)
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'
essai man
if [ -e file ]; then
...
fi
fonctionnera pour dir\file.
concerne