x-y*_*uri 8 shell bash su options
man su 说:
You can use the -- argument to separate su options from the arguments
supplied to the shell.
Run Code Online (Sandbox Code Playgroud)
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 --.
Run Code Online (Sandbox Code Playgroud)
那么,让我们看看:
[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
Run Code Online (Sandbox Code Playgroud)
我所期望的(第二个命令的输出不同):
[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
Run Code Online (Sandbox Code Playgroud)
应该问题不大。但是那里发生了什么?第二个和第三个变体似乎是要走的路,但其中一个不起作用。第四个好像不太靠谱,-可以当成su's option。
发生的事情是您提供给 shell 的第一个参数是$0参数,(通常这将是 shell 的名称)。当你这样做时它不包括在内,echo $*因为$*除了$0.
例子:
# su - graeme -c 'echo "\$0 - $0"; echo "\$* - $*"' -- sh 1 2 3
$0 - sh
$* - 1 2 3
Run Code Online (Sandbox Code Playgroud)
执行以下命令:
strace -f su graeme -c 'echo $0; echo "$*"' -- -- 1 2 3
Run Code Online (Sandbox Code Playgroud)
产生跟踪线:
strace -f su graeme -c 'echo $0; echo "$*"' -- -- 1 2 3
Run Code Online (Sandbox Code Playgroud)
因此,不知何故,在这种情况下,似乎su正在吞噬额外的东西--而不将其传递给 bash,这可能是由于错误(或至少是未记录的行为)。然而,它不会超过两个--参数:
[pid 9609] execve("/bin/bash", ["bash", "-c", "echo $0; echo \"$*\"", "1", "2", "3"], [/* 27 vars */] <unfinished ...>
Run Code Online (Sandbox Code Playgroud)
实际上@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
Run Code Online (Sandbox Code Playgroud)
如果您已经拥有要提供给 shell 的值,则不需要--3 次。Shell 参数是set可以的——任何时候都可以,而不仅仅是在调用时($0 和 -i 除外):
su - mikeserv -c 'set -- "$*" ; echo "$*" ;
set -- 4 5 6 ; echo "$*"' -- -- 7 8 9
7 8 9
4 5 6
Run Code Online (Sandbox Code Playgroud)
所有这些 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
Run Code Online (Sandbox Code Playgroud)
父 shell 的参数是set4、5 和 6,然后传递给su通过位置parameter "$@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
Run Code Online (Sandbox Code Playgroud)
使用两个 heredocs 可以帮助转义。以下是每种情况下会发生的情况:
<<HEREDOCsh 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
Run Code Online (Sandbox Code Playgroud)
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
'' $@ "$@" $@
\$@ $@ "\$@"
Run Code Online (Sandbox Code Playgroud)
"$@"IN<<HEREDOCsh 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
Run Code Online (Sandbox Code Playgroud)
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 $@
"$@" $@ \$@ $@
"\$@"
Run Code Online (Sandbox Code Playgroud)
"$@"和更多<<HEREDOCsh 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
Run Code Online (Sandbox Code Playgroud)
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
"$@" $@ \$@ $@
"\$@"
Run Code Online (Sandbox Code Playgroud)