Un script shell peut-il imprimer son argument, cité comme vous l'écrivez dans l'invite du shell?

Dans un script shell, ma compréhension est que "$@" s'étend aux arguments du script, en les citant comme nécessaire. Par exemple, cela transmet les arguments de script à gcc:

 gcc -fPIC "$@" 

En utilisant la syntaxe bash-pass-stdin <<< , "@$" ne fonctionne pas comme je l'espère.

 #!/bin/bash cat <<< "$@" 

Appel du script comme ./test.sh foo "bar baz" donne

 foo bar baz 

Je m'attendrais

 foo "bar baz" 

Existe-t-il un moyen d'écrire un script shell qui affiche ses arguments comme vous les écririez à l'invite du shell? Par exemple: un indice sur la command à utiliser ensuite, y compris les arguments de script dans le conseil.

 `"$@"` expands to the script arguments, quoting them as needed 

Non, ce n'est pas ce qui se passe. L'appel d'un programme prend une list d'arguments, chaque argument étant une string. Lorsque vous exécutez le programme shell ./test.sh foo "bar baz" , ceci crée un appel avec trois arguments: ./test.sh , foo et bar baz . (L'argument zeroth est le nom du programme, ce qui permet aux programmes de savoir sous quel nom ils sont appelés). Le cotation est une fonctionnalité du shell, et non une caractéristique des appels de programme. Le shell construit cette list lorsqu'il effectue l'appel.

"$@" copy directement la list des arguments transmis au script ou à la fonction à la list des arguments de l'appel où il est utilisé. Il n'y a aucune citation impliquée puisqu'il n'y a pas d'parsing de shell faite sur ces lists.

Dans le cat <<< "$@" , vous utilisez "$@" dans un context où une seule string est requirejse. L'opérateur <<< requirejs une string, pas une list de strings. Dans ce context, bash prend les éléments de la list et les joint avec un espace entre les deux.

Pour le debugging de script, si vous exécutez set -x ( set +x pour désactiver), cela active un mode de trace où chaque command est imprimée avant est exécutée. En bash, cette trace a des guillemets qui permettent de recoller la command dans un shell (ce n'est pas le cas de chaque implémentation sh ).

Si vous avez une string et que vous voulez la transformer en syntaxe source shell qui returnne dans la string d'origine, vous pouvez l'entourer de guillemets simples et replace chaque citation à l'intérieur de la string par '\'' .

 for x do printf %s "'${x//\'/\'\\\'\'}' " done echo 

La syntaxe de rlocation de string est ksh93 / bash / zsh / mksh-specific. En clair, vous devez faire une boucle sur la corde.

 for raw do quoted= while case "$raw" in *\'*) true;; *) false;; esac; do quoted="$quoted'\\''${raw%%\'*}" raw="${raw#*\'}" done printf %s "'$quoted$raw' " done echo 

Eh bien, "$@" s'étend à la list des parameters de position, un argument par paramètre de position.

Quand vous faites:

 set '' 'foo bar' $'blah\nblah' cmd "$@" 

cmd est invoqué avec ces 3 arguments: la string vide, foo bar et blah<newline>blah . Le shell appellera l'appel système execve() avec quelque chose comme:

 execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]); 

Si vous voulez rebuild une command line shell (c'est-à-dire du code dans le langage shell) qui reproduirait la même invocation, vous pourriez faire quelque chose comme:

 awk -vq="'" ' function shellquote(s) { gsub(q, q "\\" qq, s) return qsq } BEGIN { for (i = 1; i < ARGC; i++) { printf "%s", sep shellquote(ARGV[i]) sep = " " } printf "\n" }' cmd "$@" 

Ou avec zsh , demandant différents types de citations:

 $ set '' 'foo bar' $'blah\nblah' $ print -r -- cmd "${(q)@}" cmd '' foo\ bar blah$'\n'blah $ print -r -- cmd "${(qq)@}" cmd '' 'foo bar' 'blah blah' $ print -r -- cmd "${(qqq)@}" cmd "" "foo bar" "blah blah" $ print -r -- cmd "${(qqqq)@}" cmd $'' $'foo bar' $'blah\nblah' 

Ou avec zsh , bash ou ksh93 (ici pour bash , YMMV avec d'autres shells):

 $ set '' 'foo bar' $'blah\nblah' $ printf cmd; printf ' %q' "$@"; printf '\n' cmd '' foo\ bar $'blah\nblah' 

Vous pouvez également utiliser l'option xtrace du shell qui amène le shell à imprimer ce qu'il va exécuter:

 $ (PS4=; set -x; : cmd "$@") : cmd '' 'foo bar' 'blah blah' 

Au-dessus, nous avons exécuté la command : no-op avec cmd et les parameters de position comme argument. Ma coquille les a imprimés d'une manière bien citée appropriée pour réinjecter à la coquille. Toutes les coquilles ne font pas ça.

"$@" développe les arguments du script, en les citant au besoin

Eh bien, en quelque sorte. Pour des raisons pratiques, cela devrait être assez proche, et le manuel de reference dit que "$@" is equivalent to "$1" "$2" ...

Donc, avec les deux parameters foo et bar baz , ceux-ci seraient les mêmes:

 echo "$@" echo "$1" "$2" echo "foo" "bar baz" 

(Sauf que si les parameters contenaient des caractères spéciaux au lieu de simples strings, ils ne seraient pas étendus à nouveau après avoir augmenté $@ et $1 …)

Mais même si nous considérons $@ remplacé par les parameters entre guillemets, les guillemets ne seraient pas là pour voir echo , de même que gcc n'obtient pas les guillemets non plus.

<<< est un peu une exception à la règle "$@" == "$1" "$2" ... Il est explicitement mentionné que The result is supplied as a single ssortingng to the command on its standard input après avoir traversé expansion des parameters et des variables et suppression des devis entre autres. Alors comme d'habitude, <<< "foo" donne seulement foo en input, de la même manière somecmd "foo" ne donne que foo comme argument.

Appelant le script comme ./test.sh foo "bar baz" […] Je m'attendrais à foo "bar baz"

Si les citations restaient, il faudrait quand même être "foo" "bar baz" . Le shell ou toute command en cours d'exécution n'a aucune idée de ce que la citation était lorsque la command a été exécutée. Ou même s'il y a eu des citations à citer, l'appel système ne fait que recevoir une list de strings terminées par zéro et les guillemets ne sont qu'une fonction du langage shell. D'autres langues peuvent avoir d'autres conventions.