Passer des arguments au shell fourni

8

man su dit:

You can use the -- argument to separate su options from the arguments
supplied to the shell.

man bash dit:

--        A  --  signals  the  end of options and disables further option
          processing.  Any arguments after the -- are treated as filenames
          and arguments.  An argument of - is equivalent to --.

Eh bien, voyons:

[root ~] su - yuri -c 'echo "$*"' -- 1 2 3
2 3
[root ~] su - yuri -c 'echo "$*"' -- -- 1 2 3                                                       
2 3
[root ~] su - yuri -c 'echo "$*"' -- - 1 2 3                                                        
1 2 3
[root ~] su - yuri -c 'echo "$*"' - 1 2 3                                                           
1 2 3

Ce que j'attendais (la sortie de la deuxième commande diffère):

[root ~] su - yuri -c 'echo "$*"' -- 1 2 3
2 3
[root ~] su - yuri -c 'echo "$*"' -- -- 1 2 3                                                       
1 2 3
[root ~] su - yuri -c 'echo "$*"' -- - 1 2 3                                                        
1 2 3
[root ~] su - yuri -c 'echo "$*"' - 1 2 3                                                           
1 2 3

Probablement pas vraiment un problème. Mais que se passe-t-il là-bas? Les deuxième et troisième variantes semblent être la voie à suivre, mais l'une d'elles ne fonctionne pas. Le quatrième semble peu fiable, -peut être traité comme suune option.

x-yuri
la source
C'est étrange. J'obtiens exactement les résultats que vous attendez (et je suis également d'accord avec ces attentes). Je veux dire, dans le cas d'une deuxième invocation, j'obtiens "1 2 3" en sortie. J'utilise bash 4.2.45sur les comptes source et de destination.
Krzysztof Adamski

Réponses:

9

Ce qui se passe, c'est que le premier argument que vous fournissez au shell est le $0paramètre (généralement ce serait le nom du shell). Il n'est pas inclus lorsque vous le faites echo $*car $*chaque argument est différent de $0.

Exemple:

# su - graeme -c 'echo "\$0 - $0"; echo "\$* - $*"' -- sh 1 2 3
$0 - sh
$* - 1 2 3

Mise à jour

Effectuez la commande suivante:

strace -f su graeme -c 'echo $0; echo "$*"' -- -- 1 2 3

donne la ligne de strace:

[pid  9609] execve("/bin/bash", ["bash", "-c", "echo $0; echo \"$*\"", "1", "2", "3"], [/* 27 vars */] <unfinished ...>

Donc, il semble que dans ce cas, il suengloutisse le supplément --sans le passer à bash, probablement en raison d'un bug (ou du moins d'un comportement non documenté). Il ne va cependant pas ronger plus de deux des --arguments:

# su graeme -c 'echo $0; echo "$*"' -- -- -- 1 2 3
--
1 2 3
Graeme
la source
Ça je comprends. Je fais su - yuri -c 'echo "$*"' -- -- 1 2 3:, le shell est censé être obtenu -- 1 2 3, mais uniquement les sorties 2 3. Cela a-t-il même un sens?
x-yuri
Et quand je le fais bash -c 'echo $*' -- 1 2 3, il sort 1 2 3comme prévu.
x-yuri
@ x-yuri, mis à jour. Cela semble être un subug.
Graeme
5

En fait, la réponse de @ Graeme - et votre question - ne font que référencer les effets secondaires de la façon dont le shell les gère. "$@positional $*parameters".Ils sont attribués par le shell à ses arguments lors de l'invocation et à tout moment plus tard avec l' setutilitaire intégré . Ils peuvent être appelés à tout moment avec soit "$*"qui divise chaque position avec le premier caractère, "$IFS"soit "$@"qui cite chaque position et les divise avec tous les"$IFS."

man set

    NAME
       set  set or unset options and positional parameters

SYNOPSIS
       set [−abCefhmnuvx] [−o option] [argument...]

       set [+abCefhmnuvx] [+o option] [argument...]

       set −− [argument...]

       set o

       set +o

Si vous avez déjà les valeurs que vous alimentez le shell, vous n'avez pas besoin de --trois fois. Les paramètres du shell sont setcapables - toujours, à tout moment, pas seulement à l'appel (sauf $ 0 et -i):

su - mikeserv -c 'set -- "$*" ; echo "$*" ; 
    set -- 4 5 6 ; echo "$*"' -- -- 7 8 9

7 8 9
4 5 6

Et toutes ces citations peuvent être déroutantes. Cela simplifie un peu les choses:

( set -- 4 5 6
    su - mikeserv 4<<-\CMD /dev/fd/4 "$@"
    echo $0 "$*"
    set -- "$*"
    echo "$*"
    set -- 7 8 9
    echo "$*"
CMD
)

/dev/fd/4 4 5 6
4 5 6
7 8 9

Les arguments du shell parent sont setà 4, 5 et 6 et sont ensuite passés au sous-shell invoqué par suvia le positionnelparameter "$@array".

Notez comment je ( subshell )la commande ci-dessus - je le fais parce que je ne veux pas jouer avec mon environnement shell actuel - parce que je peux changer par inadvertance quelque chose que je préfère ne pas si je le faisais avecset.

À PROPOS DE LA REDIRECTION:

Tout d'abord, votre système Unix fonctionne avec des fichiers - autorisations de fichier, contenu de fichier, attributs de fichier. D'une manière ou d'une autre, chaque objet de données que vous utilisez peut (et, à mon avis du moins, devrait) être traité comme un fichier. La redirection pointe vers un fichier - c'est tout. A <<HERE-DOCUMENTdécrira un fichier en ligne puis le redirigera. Soit les extensions de shell sont interprétées, soit elles ne le sont pas.

Le demandeur note dans les commentaires ci-dessous que lorsqu'il tente d'utiliser cette méthode en tant rootqu'utilisateur, il reçoit une erreur d'autorisation. Quand j'ai répondu, j'ai suggéré lui chownou chgrple /dev/fd/${num}fichier spécial, mais ce n'est probablement pas la meilleure méthode. La raison pour laquelle il rencontre cette question rootest accordé des readautorisations , mais pas execute les autorisations. Vous pouvez facilement gérer cela en évitant simplement un execappel. Au lieu d'invoquer le /dev/fd/${num}fichier directement sur la ligne de commande, procédez comme suit:

su -c '. /dev/fd/'${num} ${num}<<SCRIPT 

L'utilisation de deux heredocs peut vous aider à vous échapper. Voici ce qui se passe dans tous les cas:

PAS D'INSTALLATION <<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'without set "$@" or \$@ in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
    . /dev/fd/5
    UNQUOTED
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
    )
CMD

PRODUCTION

without set "$@" or \$@ in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           $@              "$@"
PREQUOTED
/dev/fd/5
''              $@              "$@"            $@
\$@             $@              "\$@"

SET "$@"IN<<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'set "$@" and \$@ in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        set -- "$@" "\$@"
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
        . /dev/fd/5
    UNQUOTED
        set -- "$@" "\$@"
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
)
CMD

PRODUCTION

set "$@" and \$@ in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           1 2 3 4 5 6             $@              1 2 3 4 5 6
"$@"
PREQUOTED
/dev/fd/5 1 2 3 4 5 6 $@
'1              2               3               4
5               6'              '$@'            1 2 3 4 5 6
$@              $@              1 2 3 4 5 6             $@
"$@"            $@              \$@             $@
"\$@"  

ENSEMBLE "$@"ET PLUS EN<<HEREDOC

sh 3<<\CMD /dev/fd/3
    ( echo 'set "$@" and \$@ AND additional parameters in here-doc' ; echo
    set -- '1 "2" 3' 4 "5 6"
    su - mikeserv 4<<-UNQUOTED 5<<-\PREQUOTED /dev/fd/4
        set -- "$@" "\$@" '7 "8" 9' 10 "11 12"
        echo UNQUOTED; echo $0 "$*"
        printf "%s\\t\\t%s\\t\\t%s\\t\\t%s\\n" $(printf "'%s' " "$@") \\
                $@ '$@' "$@" '"$@"' "'$@'" \$@ '\$@' "\$@" '"\$@"'
        . /dev/fd/5
    UNQUOTED
        set -- "$@" "\$@" '13 "14" 15' 16 "17 18"
        echo PREQUOTED ; echo $0 "$*"
        printf "%s\t\t%s\t\t%s\t\t%s\n" $(printf "'%s' " "$@") \
                $@ '$@' "$@" '"$@"' \$@ '\$@' "\$@" '"\$@"'
    PREQUOTED
    )
CMD

PRODUCTION

set "$@" and \$@ AND additional parameters in here-doc

UNQUOTED
/dev/fd/3 1 2 3 4 5 6
1 "2" 3         4               5 6             1
2               3               4               5
6               1 "2" 3 4 5 6           1 2 3 4 5 6             "1 "2" 3 4 5 6"
'1 2 3 4 5 6'           1 2 3 4 5 6             7 "8" 9         10
11 12           $@              1 2 3 4 5 6             7 "8" 9
10              11 12           "$@"
PREQUOTED
/dev/fd/5 1 2 3 4 5 6 7 "8" 9 10 11 12 $@ 13 "14" 15 16 17 18
'1              2               3               4
5               6'              '7              "8"
9'              '10'            '11             12'
'$@'            '13             "14"            15'
'16'            '17             18'             1 2 3 4 5 6
7 "8" 9         10              11 12           $@
13 "14" 15              16              17 18           $@
1 2 3 4 5 6             7 "8" 9         10              11 12
$@              13 "14" 15              16              17 18
"$@"            $@              \$@             $@
"\$@"  
mikeserv
la source
Le problème est que votre premier script me donne "8 9\n4 5 6\n". Je cours debian 6, bash-4.1.5et su.
x-yuri
@ x-yuri - et le second, qui évite tout le désordre de citation?
mikeserv
Si courir , rootil dit: -su: /dev/fd/4: Permission denied. Tu sais ce que ça veut dire, au fait? Sinon, il sort comme vous le dites, mais il ne répond pas à la question. La question concerne l'utilisation de --et -.
x-yuri
@ x-yuri Je pense que cela signifie que vous devriez chown /dev/fd/4pour la durée dont vous en aurez besoin, ou tout simplement chgrp. Je n'ai pas beaucoup de temps pour tester en ce moment. Mais c'est un peu à côté du point, comme pour l'autre, vous n'avez pas besoin de passer d'arguments à la fin - travaillez simplement votre citation. Voyez maintenant?
mikeserv
Si nous omettons le problème de sune pas pouvoir travailler avec redirigé stdin, passer des arguments est encore mieux que de les injecter dans la commande. Parce que dans ce dernier cas, vous devez leur échapper.
x-yuri