Passando argumentos para o Shell Su-Fornecido

8

man su diz:

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

man bash diz:

--        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 --.

Bem, então, vamos ver:

[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

O que eu esperava (a saída do segundo comando difere):

[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

Provavelmente não é um grande problema. Mas o que está acontecendo lá? A segunda e a terceira variantes parecem o caminho a seguir, mas uma delas não funciona. O quarto parece não confiável, -pode ser tratado como suopção.

x-yuri
fonte
Isso é estranho. Recebo exatamente os resultados esperados (e também concordo com essas expectativas). Quero dizer, no caso de uma segunda invocação, recebo "1 2 3" como saída. Estou usando bash 4.2.45as contas de origem e de destino.
precisa saber é o seguinte

Respostas:

9

O que está acontecendo é que o primeiro argumento que você fornece ao shell é o $0parâmetro (normalmente esse seria o nome do shell). Não está incluído quando você faz, echo $*pois $*todos os argumentos são separados $0.

Exemplo:

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

Atualizar

Executando o seguinte comando:

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

produz a linha de strace:

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

Então, de alguma forma, parece que, neste caso, suestá devorando o extra --sem passar para o bash, possivelmente devido a um bug (ou pelo menos comportamento não documentado). No entanto, não vai consumir mais do que dois dos --argumentos:

# su graeme -c 'echo $0; echo "$*"' -- -- -- 1 2 3
--
1 2 3
Graeme
fonte
Isso eu entendo. Eu faço su - yuri -c 'echo "$*"' -- -- 1 2 3:, o shell supostamente recebe -- 1 2 3, mas apenas produz 2 3. Isso faz algum sentido?
X-yuri
E quando eu faço bash -c 'echo $*' -- 1 2 3, sai 1 2 3como esperado.
X-yuri
@ x-yuri, atualizado. Isso parece ser um subug.
Graeme
5

Na verdade, a resposta de @ Graeme - e sua pergunta - estão apenas referenciando os efeitos colaterais de como o shell lida. "$@positional $*parameters".Estes são atribuídos pelo shell aos seus argumentos na chamada e a qualquer momento depois com o setutilitário interno. Eles podem ser chamados a qualquer momento, com o "$*"qual divide cada posição com o primeiro caractere "$IFS"ou "$@"que cita cada posição e os divide com todos os"$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

Se você já possui os valores que está alimentando com a concha, não precisa --três vezes. Os parâmetros do shell são setcapazes - sempre, a qualquer momento, não apenas na chamada (exceto $ 0 e -i):

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

7 8 9
4 5 6

E todas essas citações de shell podem ser confusas. Isso simplifica um pouco as coisas:

( 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

Os argumentos do shell pai são set4, 5 e 6 e depois são passados ​​para o subshell invocado por sumeio da posiçãoparameter "$@array".

Observe como eu ( subshell )o comando acima - faço isso porque não quero mexer no meu ambiente de shell atual - porque posso inadvertidamente alterar algo que preferiria não fazer se fizesse comset.

SOBRE REDIRECÇÃO:

Primeiro de tudo, seu sistema Unix funciona com arquivos - permissões de arquivos, conteúdo e atributos de arquivos. De uma maneira ou de outra, todos os objetos de dados que você usa podem (e, pelo menos na minha opinião, deveriam) ser endereçados como um arquivo. O redirecionamento aponta para um arquivo - é tudo. A <<HERE-DOCUMENTdescreverá um arquivo em linha e o redirecionará. As expansões de shell são interpretadas ou não.

O autor da pergunta observa nos comentários abaixo que, quando ele tenta usar esse método como rootusuário, ele recebe um erro de permissão. Quando respondi, sugeri que ele chownou chgrpo /dev/fd/${num}arquivo especial, mas esse provavelmente não é o melhor método. A razão pela qual ele encontra esse problema rooté conceder readpermissões, mas não execute permissões. Você pode lidar com isso facilmente, apenas evitando uma execchamada. Em vez de chamar o /dev/fd/${num}arquivo diretamente na linha de comando, faça:

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

O uso de dois heredocs pode ajudar a escapar. Aqui está o que acontece em todos os casos:

NO SET IN <<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

RESULTADO

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

RESULTADO

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             $@
"$@"            $@              \$@             $@
"\$@"  

SET "$@"E MAIS EM<<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

RESULTADO

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
fonte
O problema é que seu primeiro script me fornece "8 9\n4 5 6\n". Estou correndo debian 6, bash-4.1.5e su.
X-yuri
@ x-yuri - e a segunda, que evita toda a bagunça citada?
mikeserv
Se executado a partir rootele diz: -su: /dev/fd/4: Permission denied. Você sabe o que isso significa, a propósito? Caso contrário, ele será exibido como você diz, mas não aborda a questão. A pergunta é sobre o uso de --e -.
X-yuri
@ x-yuri Eu acho que isso significa que você deve chown /dev/fd/4pela duração que precisar, ou apenas chgrp. Não tenho muito tempo para testar agora. Mas isso é um pouco irrelevante, como foi o outro, você não precisa passar argumentos no final - basta trabalhar sua citação. Veja Agora?
mikeserv
Se omitirmos o problema por sunão podermos trabalhar com redirecionados stdin, passar argumentos ainda é melhor do que injetá-los no comando. Porque no último caso, você precisa escapar deles.
X-yuri