将参数传递给su提供的shell


8

man su 说:

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

man bash 说:

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

好吧,让我们看看:

[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

我所期望的(第二个命令的输出有所不同):

[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

可能不是很多问题。但是那里发生了什么?第二个和第三个变体似乎很可行,但是其中一个不起作用。第四个似乎不可靠,-可以视为su的选项。


那很奇怪。我完全可以得到您期望的结果(我也同意那些期望)。我的意思是,在第二次调用的情况下,我确实得到了“ 1 2 3”作为输出。我bash 4.2.45同时在源帐户和目标帐户上使用。
Krzysztof Adamski 2014年

Answers:


9

发生的情况是,您提供给外壳程序的第一个参数是$0参数(通常这是外壳程序的名称)。这样做时不包括在内,echo $*因为$*每个参数都与分开$0

例:

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

更新资料

执行以下命令:

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

产生strace线:

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

因此,在某种情况下,似乎是在su吞噬多余的东西--而没有将其传递给bash,这可能是由于错误(或者至少是未记录的行为)造成的。但是,它不会占用两个以上的--参数:

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

我了解。我这样做:su - yuri -c 'echo "$*"' -- -- 1 2 3假设外壳会获取-- 1 2 3,但仅输出2 3。它甚至有意义吗?
x-yuri 2014年

当我这样做时bash -c 'echo $*' -- 1 2 3,它会1 2 3按预期输出。
x-yuri 2014年

@ x-yuri,已更新。这似乎是一个su错误。
Graeme

5

实际上,@ Graeme的答案-和您的问题-只是引用了shell处理方式的副作用。"$@positional $*parameters".这些作用是在调用时由shell分配给其参数的,并且在以后的任何时间都使用内置set实用程序。可以随时调用它们,要么"$*"将每个位置拆分为第一个字符,"$IFS"要么用"$@"引号将每个位置拆分为所有"$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

如果已经有了要输入外壳程序的值,则不需要--三遍。Shell参数set能够-始终在任何时候而不只是在调用时($ 0和-i除外):

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

7 8 9
4 5 6

而且所有这些shell引用都可能令人困惑。这样可以简化一些事情:

( 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

父shell的参数是set4、5和6,然后传递给su通过位置调用的子shellparameter "$@array".

请注意我( subshell )上面的命令是如何执行的-之所以这样做是因为我不想弄乱我当前的shell环境-因为我会无意中更改一些我宁愿做的事情set.

关于重定向:

首先,您的Unix系统可以处理文件-文件权限,文件内容,文件属性。可以以一种或另一种方式,将您使用的每个数据对象(至少应该以我的观点)作为文件进行寻址。重定向指向一个文件-仅此而已。A <<HERE-DOCUMENT将内联描述一个文件,然后将其重定向。解释外壳扩展或不解释外壳扩展。

询问者在下面的注释中指出,当他尝试以root用户身份使用此方法时,将收到权限错误。当我回答我建议他chown还是chgrp/dev/fd/${num}特殊的文件,但是这可能不是最好的方法。他遇到此问题的原因root是授予read权限,而不是 execute权限。您可以避免exec通话,轻松解决此问题。与其/dev/fd/${num}直接在命令行上调用文件,不如:

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

使用两个heredocs可以帮助转义。在每种情况下都会发生以下情况:

没有设置 <<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

输出值

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

设置"$@"<<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

输出值

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

设置"$@"和更多<<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

输出值

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

问题是您的第一个脚本给了我"8 9\n4 5 6\n"。我跑debian 6bash-4.1.5su
x-yuri 2014年

@ x-yuri-第二个避免所有引用混乱?
mikeserv 2014年

如果从运行root它说:-su: /dev/fd/4: Permission denied。顺便说一句,你知道那是什么意思吗?否则,它将按照您说的那样输出,但没有解决问题。问题是关于使用---
x-yuri 2014年

@ x-yuri我认为这意味着您应该chown /dev/fd/4在需要它的持续时间内,或者只是chgrp。我现在没有太多时间来测试。但这有点不重要,因为其他方法根本不需要在末尾传递参数-只需引用即可。现在看到吗?
mikeserv 2014年

如果我们忽略了su无法使用redirect的问题stdin,则传递参数还是更好的选择,然后将其注入命令中。因为在后一种情况下,您需要将其转义。
x-yuri 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.