Bash -c avec paramètres positionnels

15

Habituellement, $0dans un script est défini sur le nom du script, ou sur ce qu'il a été appelé (y compris le chemin d'accès). Cependant, si j'utilise bashavec l' -coption, $0est défini sur le premier des arguments passés après la chaîne de commande:

bash -c 'echo $0 ' foo bar 
# foo 

En effet, il semble que les paramètres de position aient été décalés, mais y compris $0. Cependant, shiftdans la chaîne de commande n'affecte pas $0(comme d'habitude):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

Pourquoi ce comportement apparemment étrange pour les chaînes de commande? Notez que je cherche la raison, la raison d'être de la mise en œuvre d'un tel comportement étrange.


On pourrait supposer qu'une telle chaîne de commande n'aurait pas besoin du $0paramètre tel qu'il est généralement défini, donc pour l'économie, il est également utilisé pour les arguments normaux. Cependant, dans ce cas, le comportement de shiftest étrange. Une autre possibilité est celle qui $0est utilisée pour définir le comportement des programmes (a la bashappelé as shou vimappelé as vi), mais cela ne peut pas l'être, car $0ici, on ne le voit que dans la chaîne de commande et non par les programmes appelés à l'intérieur. Je ne peux penser à aucune autre utilisation $0, donc je ne peux pas l'expliquer.

muru
la source
En remarque, j'ai vu l'utilisation de -pour l' $0argument comme un idiome, comme dans sh -c 'foo $1 $2' - a b. De cette façon, cela semble assez normal (une fois que vous avez découvert ce que cela -signifie, c'est-à-dire)
Volker Siegel
Vous pouvez également echo 'echo the other side of this pipe globs "$@"' | sh -s -- *, bien que, malheureusement, ce ne $0soit généralement pas un paramètre définissable avec l' -soption tream ... Il peut être utilisé de nombreuses manières xargsgénéralement, cependant. Et d'autres d'ailleurs.
mikeserv
@VolkerSiegel Il aurait été plus normal --, alors cela aurait pu avoir l'interprétation habituelle de "d'ici commence les arguments", vu dans certains autres programmes. Là encore, cela pourrait confondre ceux qui ne sont pas familiers avec l' -cidée de --réellement avoir cette interprétation.
muru

Réponses:

10

Cela vous donne la possibilité de définir / choisir $0lorsque vous utilisez un script en ligne. Sinon, ce $0serait juste bash.

Ensuite, vous pouvez faire par exemple:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

Tous les obus ne le faisaient pas. Le obus Bourne l'a fait. Le shell Korn (et Almquist) a choisi de faire passer le premier paramètre à la $1place. POSIX a finalement opté pour la méthode Bourne, kshet les ashdérivés y sont revenus plus tard (plus à ce sujet sur http://www.in-ulm.de/~mascheck/various/find/#shell ). Cela signifiait que pendant longtemps sh(qui, selon le système, était basé sur le shell Bourne, Almquist ou Korn), vous ne saviez pas si le premier argument était entré $0ou $1, donc pour la portabilité, vous deviez faire des choses comme:

sh -c 'echo foo in "$1"' foo foo

Ou:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Heureusement, POSIX a spécifié le nouveau comportement où va le premier argument $0, nous pouvons donc désormais le faire de manière portable:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
Stéphane Chazelas
la source
Je viens couru: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtsur Ubuntu 14.04, bash version 4.3.11(1)-release, et je suis arrivé: txt files are *.txt. Oo
muru
2
@muru, qui est correct (et vous n'avez probablement pas de txtfichiers dans le répertoire courant). Voir aussibash -c 'echo "${1?}"' foo
Stéphane Chazelas
Ah oui. Voilà un exemple pratiquement utile.
muru
1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtest incroyablement inventif!
iruvar
Ou vous utilisez une solution de contournement complètement robuste (suggérée par Stéphane Chazelas dans comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... haha ...
mikeserv
3

Ce comportement est défini par POSIX :

sh -c nom_commande [argument ...]

Lisez les commandes de l'opérande command_string. Définissez la valeur du paramètre spécial 0 (voir Paramètres spéciaux ) à partir de la valeur de l'opérande command_name et des paramètres positionnels ($ 1, $ 2, etc.) en séquence à partir des opérandes d'arguments restants.

Quant à savoir pourquoi vous souhaitez ce comportement: cela atténue l'écart entre un script et une -cchaîne. Vous pouvez directement convertir entre les deux sans aucun changement de comportement. D'autres domaines reposent sur le fait qu'ils sont identiques.

Il est également conforme au fonctionnement des arguments de programme en général: cela revient finalement à appeler l'une des execfonctions, dans laquelle le premier argument fourni est également $0, et tout aussi souvent cet argument est le même que l'exécutable que vous exécutez. Parfois, cependant, vous voulez une valeur spéciale là-bas, et il n'y aurait tout simplement pas d'autre moyen de l'obtenir. Étant donné que l'argument existe, il doit correspondre à quelque chose et l'utilisateur doit pouvoir définir ce que c'est.

Cette cohérence (et probablement un accident historique) mène à la situation que vous trouvez.

Michael Homer
la source
Pouvez-vous donner un exemple (espérons-le réel) du deuxième paragraphe?
muru
Attention à l'analogie avec argv[0]. $0est uniquement défini sur le argv[0]passé à execve()quand il est comme shou bash. Pour les scripts, $0est défini sur le chemin donné comme argument 1, 2 ... à l'interpréteur (et pour les scripts exécutés directement, cela vient du premier argument de chemin à execve ()).
Stéphane Chazelas