Quelle est la bonne façon de citer $ (commande $ arg)?


17

Il est grand temps de résoudre cette énigme qui me dérange depuis des années ...

Je l'ai rencontré de temps en temps et j'ai pensé que c'était la voie à suivre:

$(comm "$(arg)")

Et j'ai pensé que mon point de vue était fortement soutenu par l'expérience. Mais je n'en suis plus si sûr. Shellcheck ne peut pas non plus se décider. C'est à la fois:

"$(dirname $0)"/stop.bash
           ^-- SC2086: Double quote to prevent globbing and word splitting.

Et:

$(dirname "$0")/stop.bash
^-- SC2046: Quote this to prevent word splitting.

Quelle est la logique derrière?

(Il s'agit de la version 0.4.4 de Shellcheck, btw.)


1
Citez toutes les substitutions.
Ignacio Vazquez-Abrams

3
Juste comme ça "$(dirname "$0")"/stop.bash:? Cela semble fonctionner ... Quelle est l'histoire?
Tomasz

1
bash est assez intelligent pour savoir quand le contexte a changé.
Ignacio Vazquez-Abrams

1
Pensez-y comme ceci:: shell_level_1 $(shell_level_2) shell_level_1lorsque vous êtes à l'intérieur du, $(....)vous entrez un "sous-niveau" de shell, MAIS vous pouvez l'écrire comme si vous étiez au niveau primaire (c'est-à-dire que vous pouvez directement écrire "au lieu de \", etc.). ex: touch "/tmp/a file" ; echo "its size is: $(find "/tmp/a file" -ls | awk '{print $5}) ..." : if you used backticks you'd have to trouver \ "/ tmp / un fichier \" `et print \$5. Avec $(...)pas besoin: les adapte shell au nouveau niveau et vous pouvez écrire directement comme si votre interprète est maintenant à ce niveau aussi.
Olivier Dulac

"Shellcheck ne peut pas non plus se décider." - Les deux pistes ont deux messages différents et pointent à des endroits différents. Il veut les deux.
Izkata

Réponses:


45

Vous devez utiliser "$(somecmd "$file")".

Sans les guillemets, un chemin avec un espace sera divisé dans l'argument to somecmdet ciblera le mauvais fichier. Vous avez donc besoin de citations à l'intérieur.

Tous les espaces dans la sortie de somecmdprovoqueront également le fractionnement, vous avez donc besoin de guillemets à l'extérieur de la substitution de commande entière.

Les citations à l'intérieur de la substitution de commande n'ont aucun effet sur les citations à l'extérieur. Le manuel de référence de Bash n'est pas trop clair à ce sujet, mais BashGuide le mentionne explicitement . Le texte dans POSIX l' exige également, car "tout script shell valide" est autorisé à l'intérieur $(...):

Avec le $(command)formulaire, tous les caractères suivant la parenthèse ouvrante à la parenthèse fermante correspondante constituent la commande. Tout script shell valide peut être utilisé pour la commande, à l'exception d'un script composé uniquement de redirections qui produit des résultats non spécifiés.


Exemple:

$ file="./space here/foo"

une. Pas de devis, dirnametraite les deux ./spaceet here/foo:

$ printf "<%s>\n" $(dirname $file)
<.>
<here>

b. Citations à l'intérieur, dirnameprocessus ./space here/foo, dons ./space here, divisés en deux:

$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>

c. Les citations à l'extérieur dirnametraitent les deux ./spaceet les here/foosorties sur des lignes distinctes, mais maintenant les deux lignes forment un seul argument :

$ printf "<%s>\n" "$(dirname $file)"
<.
here>

ré. Citations à l'intérieur et à l'extérieur, cela donne la bonne réponse:

$ printf "<%s>\n" "$(dirname "$file")"
<./space here>

(cela aurait peut-être été plus simple si dirnameseulement traité le premier argument, mais cela ne montrerait pas la différence entre les cas a et c.)

Notez qu'avec dirname(et éventuellement d'autres), vous devez également ajouter --, pour éviter que le nom de fichier ne soit pris en option au cas où il commencerait par un tiret, alors utilisez "$(dirname -- "$file")".


16
Remarque: En général, les guillemets ne sont pas imbriqués dans bash; mais dans ce cas, le $( )crée un nouveau contexte, donc les guillemets à l'intérieur sont indépendants des guillemets à l'extérieur. Et vous voulez généralement des guillemets dans les deux contextes.
Gordon Davisson
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.