Pourquoi des guillemets sont-ils nécessaires pour l'argument des fichiers lors de l'appel de ce script Bash?

13

Je suis assez nouveau dans les scripts Bash. J'ai un "script de test", que j'ai utilisé comme base pour un script plus avancé / utile:

#!/bin/bash
files=$1
for a in $files
do
    echo "$a"
done

Lorsque j'appelle cela sans guillemets, il prend simplement un fichier dans un répertoire:

testscript *.txt

Mais quand je l'appelle avec des guillemets, cela fonctionne correctement et sélectionne tous les fichiers texte:

testscript '*.txt'

Qu'est-ce qui se passe ici?

gornvix
la source
Pour être très, très clair, la bonne façon de résoudre ce problème est d'exécuter for a in "$@"; do(ou for a; do) dans votre script, laissant ainsi le globbing au shell externe, et non de laisser de côté les guillemets.
Charles Duffy
Cela vaut bien un coup d'oeil. guide.bash.academy
vascowhite

Réponses:

29

Lorsque vous appelez un programme

testscript *.txt

alors votre shell fait l'expansion et calcule toutes les valeurs. Il est donc possible d'appeler efficacement votre programme

testscript file1.txt file2.txt file3.txt file4.txt

Maintenant, votre programme ne regarde que $1et ne fonctionne donc que sur file1.txt.

En citant sur la ligne de commande, vous passez la chaîne littérale *.txtau script, et c'est ce qui est stocké dans $1. Votre forboucle l'agrandit ensuite.

Normalement, vous utiliseriez "$@"et non $1dans des scripts comme celui-ci.

Il s'agit d'un "piège" pour les personnes provenant de scripts CMD, où le shell de commande ne fait pas de globalisation (comme on le sait) et passe toujours la chaîne littérale.

Stephen Harris
la source
6
Juste pour préciser (pour les personnes autres que l'auteur de la réponse ci - dessus), en utilisant "$@"(par opposition à $@ou $1 $2 $3) provoquera chaque nom de fichier à rappeler "file1.txt" "file2.txt"etc. Pour file1.txtcela n'a pas de sens, mais si vous avez my file.txt, la citation est essentiel pour empêcher le shell analyse pour le transformer en deux noms de fichiers, un nommé myet un nommé file.txt. Citez toujours l'entrée utilisateur et l'expansion globale de peur que vous ne soyez très mécontent un jour.
Seth Robertson
2
Et ce n'est pas seulement théorique - Mac OS X a été livré avec un script de mise à jour qui ne citait pas correctement les arguments et a fini par supprimer les disques durs des gens dans certaines circonstances.
moelleux
2
@fluffy, avez-vous un lien à ce sujet?
Wildcard
@Wildcard Malheureusement, je ne trouve aucun article à ce sujet, mais c'était une grande nouvelle dans le monde de la technologie quand cela s'est produit. Je veux dire que c'était en 2003/2004 ou à peu près à l'époque, quand Apple commençait toujours à être un distributeur UNIX.
moelleux
1
@wildcard Ah, je l'ai trouvé! xlr8yourmac.com/OSX/itunes2_erased_drives.html - c'était en fait un script de mise à niveau iTunes qui était le coupable.
moelleux
7

Sans guillemets, le shell se développe *.txtavant d'appeler le script, tout $1comme le premier fichier qui est développé. Tous les txtfichiers sont des arguments pour votre script à ce stade (en supposant qu'il n'y en a pas trop).

Avec des guillemets, cette chaîne est passée sans être développée dans le script, ce qui permet ensuite de forfaire l'expansion, comme vous l'espérez.

Eric Renouf
la source