我有一个脚本,我写的是切换到root或以root身份运行命令而没有密码。我编辑了我的/ etc / sudoers文件,以便我的用户[matt]有权运行/ bin / su而没有密码。这是我的脚本“s”内容:
matt: ~ $ cat ~/bin/s
#!/bin/bash
[ "$1" != "" ] && c='-c'
sudo su $c "$*"
如果没有参数[只是s
],它基本上会调用sudo su
,它会在没有密码的情况下转到root。但是如果我把paramaters,$ c变量等于“-c”,这使得su执行一个命令。
除非我需要使用空格,否则效果很好。例如:
matt: ~ $ touch file\ with\ spaces
matt: ~ $ s chown matt file\ with\ spaces
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file with spaces'
chown: cannot access 'file': No such file or directory
chown: cannot access 'with': No such file or directory
chown: cannot access 'spaces': No such file or directory
matt: ~ $ s chown matt 'file\ with\ spaces'
matt: ~ $
我该如何解决这个问题?
另外, $ * 和 $ @ 之间有什么区别?
答案 0 :(得分:8)
啊,引用很有趣。通常,@ John建议的方法适用于此:使用"$@"
,并且它不会尝试解释(并且混淆)参数中的空格和其他有趣的字符。但是,在这种情况下,这将无法工作,因为su的-c选项需要将整个命令作为单个参数传递,然后它将启动一个解析命令的新shell(包括被空格等混淆) 。为了避免这种情况,您实际上需要重新引用要传递给su -c
的字符串中的参数。 bash的printf
内置可以做到这一点:
#!/bin/bash
if [ $# -gt 0 ]; then
sudo su -c "$(printf "%q " "$@")"
else
sudo su
fi
让我回顾一下这里发生的事情:
s chown matt file\ with\ spaces
printf "%q " "$@"
命令时,它会用脚本的参数替换"$@"
,参数分解完整。它相当于printf "%q " "chown" "matt" "file with spaces"
。printf
将格式字符串“%q”解释为“以引用形式打印每个剩余参数,后面带一个空格”。它打印:“chown matt file \ with \ spaces”,基本上重建原始命令行(它最后有一个额外的空间,但事实证明这不是问题)。$()
构造周围有双引号,它将被视为sudo的单个参数)。这相当于运行sudo su -c "chown matt file\ with\ spaces "
。sudo
运行su
,并传递其获得的参数列表的其余部分,包括完全转义的命令。su
运行一个shell,它还会传递其参数列表的其余部分。-c
:chown matt file\ with\ spaces
的参数获得的命令。在解析它的正常过程中,它会将非转义空间解释为参数之间的分隔符,并将转义空格解释为参数的一部分,并且它将忽略末尾的额外空间。chown
,参数“matt”和“file with spaces”。这符合您的期望。不是bash解析hoot吗?
答案 1 :(得分:3)
"$*"
将所有位置参数($1
,$2
,...)收集到一个单词中,用一个空格分隔(更一般地说,$IFS
的第一个字符)。请注意,在shell术语中,单词可以包含任何字符,包括空格:"foo bar"
或foo\ bar
解析为单个单词。例如,如果有三个参数,则"$*"
等同于"$1 $2 $3"
。如果没有参数,那么"$*"
相当于""
(一个空字)。
"$@"
是一种特殊的语法,可以扩展到位置参数列表,每个参数都在自己的单词中。例如,如果有三个参数,则"$@"
等同于"$1" "$2" "$3"
。如果没有参数,那么"$@"
等同于 nothing (一个空列表,而不是一个单词为空的列表)。
"$@"
几乎总是您要使用的内容,而不是"$*"
或不加引号$*
或$@
(后两个完全等效并执行文件名生成) (也称为globbing)和所有位置参数上的单词分割。)
还有一个问题,就是su
,除了一个shell命令作为-c
的参数,并且你传递了多个单词。你已经有了a detailed explanation of getting the quoting right,但是让我补充一下如何做正确的建议,这可以避免双重引用问题。您还可以参考https://unix.stackexchange.com/questions/3063/how-do-i-run-a-command-as-the-system-administrator-root了解sudo
和su
的更多背景信息。
sudo
已经以root身份运行命令,因此无需调用su
。如果您的脚本没有参数,您可以直接运行shell;除非您的sudo
版本很旧,否则可以选择:sudo -s
。所以你的脚本可以是:
#!/bin/sh
if [ $# -eq 0 ]; then set -- -s; else set -- -- "$@"; fi
exec sudo "$@"
(其他部分是处理以-
开头的命令的罕见情况。)
我不会打扰这么简短的剧本。以root身份运行命令是不寻常的,并且有足够的风险,输入三个额外的字符应该不是问题。以root身份运行shell更加不寻常和冒险,当然还需要六个字符。