J'essaie d'écrire une instruction if
pour tester s'il existe des fichiers correspondant à un certain modèle. S'il y a un fichier texte dans un répertoire, il doit exécuter un script donné.
Mon code actuellement:
if [ -f /*.txt ]; then ./script fi
Veuillez donner quelques idées; Je souhaite exécuter le script uniquement s'il existe un .txt
dans le répertoire.
[ -f /*.txt ]
retournerait vrai seulement s'il y a n (et un seul) fichier non caché dans /
dont le nom se termine par .txt
et si ce fichier est un fichier normal ou un lien symbolique vers un fichier normal.
En effet, les caractères génériques sont développés par le shell avant d'être transmis à la commande (ici [
).
Donc, s'il y a un /a.txt
et /b.txt
, [
recevra 5 arguments: [
, -f
, /a.txt
, /b.txt
et ]
. [
se plaindrait alors que -f
reçoit trop d'arguments.
Si vous souhaitez vérifier que le *.txt
le modèle se développe en au moins un fichier non caché (normal ou non):
shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"
shopt -s nullglob
est bash
spécifique, mais des shells comme ksh93
, zsh
, yash
, tcsh
ont des instructions équivalentes.
Notez qu'il trouve ces fichiers en lisant le contenu du répertoire, il n'essaie pas du tout d'accéder à ces fichiers, ce qui le rend plus efficace que les solutions qui appellent des commandes comme ls
ou stat
sur cette liste de fichiers calculée par le Shell.
L'équivalent sh
standard serait:
set -- [*].txt *.txt
case "$1$2" in
('[*].txt*.txt') ;;
(*) shift; script "$@"
esac
Le problème est qu'avec les shells Bourne ou POSIX, si un modèle ne correspond pas, il se développe lui-même. Donc si *.txt
se développe en *.txt
, vous ne savez pas si c'est parce qu'il n'y a pas de .txt
fichier dans le répertoire ou parce qu'il y a un fichier appelé *.txt
. En utilisant [*].txt *.txt
permet de distinguer les deux.
Vous pouvez toujours utiliser find
:
find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script
Explication:
find .
: recherche dans le répertoire courant-maxdepth 1
: ne recherche pas les sous-répertoires-type f
: recherche uniquement les fichiers standardname "*.txt"
: recherche les fichiers se terminant par .txt
2>/dev/null
: redirige les messages d'erreur vers /dev/null
| grep -q .
: grep pour n'importe quel caractère, retournera false si aucun caractère n'a été trouvé.&& ./script
: Exécuter ./script
uniquement si la commande précédente a réussi ( &&
)Une solution possible est également intégrée à Bash compgen
. Cette commande renvoie toutes les correspondances possibles pour un modèle de globalisation et possède un code de sortie indiquant si des fichiers correspondent.
compgen -G "/*.text" > /dev/null && ./script
J'ai trouvé cette question en cherchant des solutions plus rapides.
Voici une doublure pour le faire:
$ ls
file1.pl file2.pl
$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists
$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist
Cette approche utilise le ||
et &&
opérateurs en bash. Ce sont les opérateurs "ou" et "et".
Donc, si la commande stat renvoie un $?
égal à 0 alors le premier echo
est appelé, s'il renvoie un 1, alors le second echo
est appelé.
# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1
# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0
Cette question est largement couverte sur stackoverflow:
Comme le souligne Chazelas, votre script échouerait si l'expansion générique correspond à plusieurs fichiers.
Cependant, il y a une astuce que j'utilise (même je ne l'aime pas beaucoup) pour se déplacer:
PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi
Comment ça fonctionne?
L'expansion des caractères génériques correspondra à un tableau de noms de fichiers, nous obtenons le premier s'il y en a, sinon null si aucune correspondance.
Aussi simple que:
cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi
wc -l
compte les lignes du caractère générique étendu.
J'aime la solution de tableau précédente, mais cela pourrait devenir un gaspillage avec un grand nombre de fichiers - le Shell utiliserait beaucoup de mémoire pour construire le tableau, et seul le premier élément serait testé.
Voici une structure alternative que j'ai testée hier:
$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no
La valeur de sortie du grep semble déterminer le chemin à travers la structure de contrôle. Cela teste également avec des expressions régulières plutôt qu'avec des modèles Shell. Certains de mes systèmes ont la commande "pcregrep" qui permet des correspondances regex beaucoup plus sophistiquées.
(J'ai édité cette réponse pour supprimer un "ls" dans la substitution de commande après avoir lu la critique ci-dessus pour l'avoir analysée.)