Pasar argumentos al shell proporcionado por su

8

man su dice:

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

man bash dice:

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

Pues bien, veamos:

[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

Lo que esperaba (la salida del segundo comando difiere):

[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

Probablemente no sea un gran problema. ¿Pero qué está pasando allí? La segunda y la tercera variantes parecen ser el camino a seguir, pero una de ellas no funciona. El cuarto parece poco confiable, -puede tratarse como suuna opción.

x-yuri
fuente
Eso es extraño. Obtengo exactamente los resultados que espera (y también estoy de acuerdo con esas expectativas). Quiero decir, en caso de una segunda invocación, obtengo "1 2 3" como salida. Estoy usando bash 4.2.45tanto en las cuentas de origen como en las de destino.
Krzysztof Adamski

Respuestas:

9

Lo que está sucediendo es que el primer argumento que proporciona al shell es el $0parámetro, (generalmente este sería el nombre del shell). No se incluye cuando lo hace, echo $*ya que $*es cada argumento aparte de $0.

Ejemplo:

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

Actualizar

Haciendo el siguiente comando:

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

rinde la línea de strace:

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

Entonces, de alguna manera, parece que en este caso suestá engullendo el extra --sin pasarlo a bash, posiblemente debido a un error (o al menos un comportamiento indocumentado). Sin embargo, no comerá más de dos de los --argumentos:

# su graeme -c 'echo $0; echo "$*"' -- -- -- 1 2 3
--
1 2 3
Graeme
fuente
Eso lo entiendo. Lo hago: su - yuri -c 'echo "$*"' -- -- 1 2 3supuestamente el shell obtiene -- 1 2 3, pero solo sale 2 3. ¿Tiene sentido?
x-yuri
Y cuando lo hago bash -c 'echo $*' -- 1 2 3, sale 1 2 3como se esperaba.
x-yuri
@ x-yuri, actualizado. Esto parece ser un suerror.
Graeme
5

En realidad, la respuesta de @ Graeme, y su pregunta, solo hacen referencia a los efectos secundarios de cómo maneja el shell. "$@positional $*parameters".Estos son asignados por el shell a sus argumentos al invocarlos y en cualquier momento posterior con la setutilidad incorporada . Pueden ser llamados en cualquier momento, ya sea "$*"que divide cada posicional con el primer carácter "$IFS"o "$@"que cita cada posicional y los divide con todos"$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 ya tiene los valores que está alimentando al shell, no es necesario que lo haga --tres veces. Los parámetros de Shell son setcapaces, siempre, en cualquier momento, no solo en la invocación (excepto $ 0 y -i):

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

7 8 9
4 5 6

Y todas esas citas de shell pueden ser confusas. Esto simplifica un poco las cosas:

( 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

Los argumentos del shell principal son set4, 5 y 6 y luego se pasan al subshell invocado por suel posicionalparameter "$@array".

Tenga en cuenta cómo uso ( subshell )el comando anterior: lo hago porque no quiero perder el tiempo con mi entorno actual de shell, porque puedo cambiar inadvertidamente algo que preferiría no hacerlo si lo hiciera conset.

ACERCA DE LA REDIRECCIÓN:

En primer lugar, su sistema Unix funciona con archivos: permisos de archivo, contenido de archivo, atributos de archivo. De una forma u otra, cada objeto de datos que usa puede (y, al menos en mi opinión, debería) abordarse como un archivo. La redirección apunta a un archivo, eso es todo. A <<HERE-DOCUMENTdescribirá un archivo en línea y luego lo redirigirá. O las expansiones de shell se interpretan o no.

El autor de la pregunta señala en los comentarios a continuación que cuando intenta utilizar este método como rootusuario, recibe un error de permisos. Cuando respondí, le sugerí él chowno chgrpel /dev/fd/${num}archivo especial, pero este probablemente no sea el mejor método. La razón se encuentra con este problema es rootse concede readpermisos pero no execute permisos. Puede manejar esto fácilmente evitando una execllamada. En lugar de invocar el /dev/fd/${num}archivo directamente en la línea de comando, haga lo siguiente:

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

El uso de dos heredocs puede ayudar a escapar. Esto es lo que sucede en todos los casos:

NO ESTABLECIDO EN <<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

SALIDA

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

SALIDA

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 "$@"Y MÁS 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

SALIDA

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
"$@"            $@              \$@             $@
"\$@"  
revs mikeserv
fuente
El problema es que tu primer script me lo da "8 9\n4 5 6\n". Estoy corriendo debian 6, bash-4.1.5y su.
x-yuri
@ x-yuri - y el segundo, que evita todo el desorden de citas
mikeserv
Si se ejecuta desde rootque dice: -su: /dev/fd/4: Permission denied. ¿Sabes lo que eso significa, por cierto? De lo contrario, sale como usted dice, pero no aborda la pregunta. La pregunta es sobre el uso de --y -.
x-yuri
@ x-yuri Creo que eso significa que debe chown /dev/fd/4durante el tiempo que va a necesitar, o simplemente chgrp. No tengo mucho tiempo para probar en este momento. Pero eso es un poco irrelevante, al igual que el otro, no necesita pasar argumentos al final de la cola, solo trabaje en su cita. ¿Vealo Ahora?
mikeserv
Si omitimos el problema de suno poder trabajar con redireccionados stdin, pasar argumentos es aún mejor que inyectarlos en el comando. Porque en el último caso necesitas escapar de ellos.
x-yuri