web-dev-qa-db-fra.com

Tester s'il existe des fichiers correspondant à un modèle afin d'exécuter un script

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.

29
user40952
[ -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.

39
Stéphane Chazelas

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 standard
  • name "*.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 ( && )
11
terdon

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.

8
Daniel Böhmer

Voici une doublure pour le faire:

$ ls
file1.pl  file2.pl

les fichiers existent

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

les fichiers n'existent pas

$ 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é.

renvoie les résultats de stat

# 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:

7
slm

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.

4
Pei Z

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.

1
Kim Johansson

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.)

0
Charlie