首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >脚本问题中的bash参数变量

脚本问题中的bash参数变量
EN

Stack Overflow用户
提问于 2011-04-27 19:57:08
回答 2查看 4.1K关注 0票数 5

我编写了一个脚本,用于切换到root或在没有密码的情况下作为root运行命令。我编辑了我的/etc/sudoers文件,这样我的用户matt就可以在没有密码的情况下运行/bin/su。这是我的剧本的内容:

代码语言:javascript
复制
matt: ~ $ cat ~/bin/s
#!/bin/bash

[ "$1" != "" ] && c='-c'

sudo su $c "$*"

如果没有参数--简单地说是s --它基本上会调用sudo su,它在没有密码的情况下进入根目录。但是如果我放置参数,$c变量等于"-c",这使得su执行一个命令。

它很好,除了当我需要使用空间的时候。例如:

代码语言:javascript
复制
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: ~ $ 

我怎么才能解决这个问题?

另外,$*$@有什么区别?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-04-27 23:58:24

啊,引用的乐趣。通常,@John建议的方法将适用于此:使用"$@",它不会试图解释(并被)参数中的空格和其他有趣的字符所迷惑。但是,在本例中,这是行不通的,因为su的-c选项希望整个命令作为单个参数传递,然后它将启动一个解析命令的新shell (包括被空格等弄糊涂)。为了避免这种情况,实际上需要重新引用要传递给su -c的字符串中的参数。bash的printf内置器可以做到这一点:

代码语言:javascript
复制
#!/bin/bash

if [ $# -gt 0 ]; then
    sudo su -c "$(printf "%q " "$@")"
else
    sudo su
fi

让我来谈谈这里发生的事情:

运行类似于s chown matt file\ with\ spaces

  • bash的命令的
  1. 将此解析为一个单词列表:"s“"chown”"matt“"file with space”。请注意,此时您键入的转义符已经被删除(尽管它们有其预期的效果:使bash将这些空格视为参数的一部分,而不是parameters).
  2. When bash之间的分隔符解析脚本中的printf "%q " "$@"命令,它将"$@"替换为脚本的参数,参数完全中断)。这相当于printf "%q " "chown" "matt" "file with spaces".
  3. printf将格式字符串"%q“解释为”以引号形式打印每个剩馀的参数,并在其后面加上空格“。它打印:"chown matt file\ with\ space ",本质上是重新构造原始命令行(它在末尾有一个额外的空间,但这不是problem).
  4. This作为参数传递给sudo (因为$()构造周围有双引号,所以它将被视为sudo的单个参数)。这相当于运行sudo su -c "chown matt file\ with\ spaces ".
  5. sudo运行su,并传递它获得的参数列表的其余部分,包括完全转义的command.
  6. su运行一个shell,它还传递其参数列表的其余部分。
  7. -- shell执行它获得的命令作为参数传递给-cchown matt file\ with\ spaces。在正常的解析过程中,它会将未转义的空间解释为参数之间的分隔符,将转义的空间解释为参数的一部分,它将忽略末尾的额外空间。
  8. shell运行chown,参数为"matt“和"file”。这符合你的预期。

bash解析不是很麻烦吗?

票数 8
EN

Stack Overflow用户

发布于 2011-04-28 00:14:09

"$*"收集所有位置参数($1$2、…)变成一个单词,用一个空格分隔(更一般地说,是$IFS的第一个字符)。注意,在shell术语中,单词可以包含任何字符,包括空格:"foo bar"foo\ bar解析单个单词。例如,如果有三个参数,那么"$*"就相当于"$1 $2 $3"。如果没有参数,那么"$*"就相当于"" (一个空单词)。

"$@"是一种特殊的语法,它扩展到位置参数列表,每个参数都在自己的单词中。例如,如果有三个参数,那么"$@"就相当于"$1" "$2" "$3"。如果没有参数,那么"$@"就等于什么都没有(空列表,没有一个单词为空的列表)。

"$@"几乎总是您想要使用的,而不是"$*",或者没有引号的$*$@ (最后两个完全等价,并执行文件名生成(a.k.a )。(所有位置参数上的单词分裂)。

还有一个问题,就是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-rootsu的更多背景信息,您也可以参考sudo

sudo已经以根用户的身份运行了一个命令,因此不需要调用su。如果您的脚本没有参数,您可以直接运行一个shell;除非您的sudo版本很旧,否则有一个选项:sudo -s。所以你的脚本可以是:

代码语言:javascript
复制
#!/bin/sh
if [ $# -eq 0 ]; then set -- -s; else set -- -- "$@"; fi
exec sudo "$@"

(其他部分是处理以-开头的命令的罕见情况。)

不过,我不想看这么短的剧本。以根用户身份运行命令是不寻常的,而且风险很大,因此输入三个额外的字符不应该是一个问题。将shell作为根运行甚至更不寻常,风险更大,当然还需要另外六个字符。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5809783

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档