Ajouter des arguments à 'bash -c'

22

Disons que je veux exécuter une commande via Bash comme ceci:

/bin/bash -c "ls -l"

Selon la page de manuel de Bash, je pourrais également l'exécuter comme ceci:

#               don't process arguments after this one
#               |   pass all unprocessed arguments to command
#               |   |
#               V   V
/bin/bash -c ls -- -l

sauf qu'il ne semble pas fonctionner (il semble ignoré). Suis-je en train de faire quelque chose de mal ou est-ce que j'interprète mal la page de manuel?

Citations pertinentes de l'homme:

Si l'option -c est présente, les commandes sont lues à partir de la chaîne. S'il y a des arguments après la chaîne, ils sont affectés aux paramètres positionnels, en commençant par $ 0.

et

A - signale la fin des options et désactive le traitement ultérieur des options. Tous les arguments après le - sont traités comme des noms de fichiers et des arguments.

Slartibartfast
la source

Réponses:

33

Vous interprétez mal la page de manuel. Premièrement, la partie concernant la --signalisation de la fin des options est sans rapport avec ce que vous essayez de faire. Le -cremplace le reste de la ligne de commande à partir de ce moment, de sorte qu'il ne passe plus du tout par la gestion des options de bash, ce qui signifie que le --serait transmis à la commande, et non pas traité par bash comme marqueur de fin d'options.

La deuxième erreur est que des arguments supplémentaires sont affectés en tant que paramètres positionnels au processus shell qui est lancé, et non passés en tant qu'arguments à la commande. Donc, ce que vous essayez de faire pourrait se faire comme:

/bin/bash -c 'echo "$0" "$1"' foo bar
/bin/bash -c 'echo "$@"' bash foo bar

Dans le premier cas, passer l'écho des paramètres $0et $1explicitement, et dans le second cas, utiliser "$@"pour développer aussi normalement que "tous les paramètres de position sauf $ 0". Notez que dans ce cas, nous devons également passer quelque chose à utiliser $0; J'ai choisi "bash" car c'est ce qui $0serait normalement, mais tout le reste fonctionnerait.

Quant à la raison pour laquelle il est fait de cette façon, au lieu de simplement passer des arguments que vous donnez directement à la commande que vous énumérez: Notez que la documentation indique « commande s sont lus à partir chaîne », au pluriel. En d'autres termes, ce schéma vous permet de faire:

/bin/bash -c 'mkdir "$1"; cd "$1"; touch "$2"' bash dir file

Mais, notez qu'une meilleure façon d'atteindre votre objectif initial pourrait être d'utiliser envplutôt que bash:

/usr/bin/env -- "ls" "-l"

Si vous n'avez besoin d'aucune des fonctionnalités fournies par un shell, il n'y a aucune raison de l'utiliser - l'utilisation envdans ce cas sera plus rapide, plus simple et moins de frappe. Et vous n'avez pas à réfléchir aussi dur pour vous assurer qu'il gérera en toute sécurité les noms de fichiers contenant des métacaractères shell ou des espaces.

godlygeek
la source
3

Je ne sais pas quel est votre objectif, mais si vous essayez simplement de construire une machine Rube Goldberg - «un engin, une invention, un appareil ou un appareil délibérément sur-conçu ou exagéré pour effectuer une tâche très simple dans un très mode compliquée »- alors essayez

sh -c 'ls $0' -l

ou

sh -c 'ls $1' supercalifragilisticexpialidocious -l

ou même

sh -c 'ls -$0' l

Vous devriez être en mesure de comprendre comment cela fonctionne à partir de la réponse de godlygeek.

Scott
la source