这是我为展示古怪行为而创建的一个例子。 我希望bash按原样传递引用的命令行参数。
john@doe:~/tmp$ cat script.sh
#! /bin/bash
set -o xtrace
$1 sleep 3
john@doe:~/tmp$ ./script.sh "echo"
+ echo sleep 3
sleep 3
john@doe:~/tmp$ ./script.sh "echo -n"
+ echo -n sleep 3
sleep 3john@doe:~/tmp$
但有时bash编辑我的字符串参数,并在符号周围添加刻度 喜欢 ;或&&
john@doe:~/tmp$ ./script.sh "echo hello ;"
+ echo hello ';' sleep 3
hello ; sleep 3
john@doe:~/tmp$ ./script.sh "echo hello && "
+ echo hello '&&' sleep 3
hello && sleep 3
这个bash规则是什么以及如何规避它?非常感谢:))
答案 0 :(得分:1)
规则是引用元字符,否则这些元字符将有不同的解释。分号是语句分隔符,&&
也是如此。引用可以轻松剪切和粘贴特定行并使其重现原始效果。
想想
的区别echo hello '&&'
与
echo hello &&
第一个回声两个字,第二个等待更多输入。你称之为quirky的引用按照你的设计工作,就是你真正想要的,不,除了黑客攻击源代码之外没有办法解决它。
答案 1 :(得分:1)
手册中概述了该规则:Section Shell Operation。
你会读到:
以下是shell操作时的简要说明 读取并执行命令。基本上,shell执行以下操作:
- 从文件中读取其输入(请参阅Shell Scripts),从作为参数提供的字符串读取
-c
调用选项(请参阅Invoking Bash),或从用户终端读取。 / LI>- 将输入分解为单词和运算符,遵守Quoting中描述的引用规则。这些令牌被分开 元字符。别名扩展由此步骤执行(请参阅 Aliases)。
- 将令牌解析为简单和复合命令(请参阅Shell Commands)。
- 执行各种shell扩展(请参阅Shell Expansions),将扩展的标记分解为文件名列表(请参阅Filename Expansion)以及命令和参数。
- 执行任何必要的重定向(请参阅Redirections)并从参数列表中删除重定向运算符及其操作数。
- 执行命令(参见Executing Commands)。
- (可选)等待命令完成并收集其退出状态(请参阅Exit Status)。
醇>
所以,你用脚本:
#! /bin/bash
$1 sleep 3
执行./script "echo hello ;"
时会发生什么?
;
中的控件操作符echo hello ;
。
此时,它只会看到三个令牌$1
,sleep
和3
。$1
已扩展为echo
,hello
和;
(而不是此处的分号一个参数,它不被理解为控制操作符,因为控制操作符的扫描已经在步骤2中完成,不再执行了。)echo
,hello
,;
和sleep
的命令3
。所以它很高兴地回应hello ; sleep 3
。echo
后收集退出状态(非常可能成功)。有两种方法可以避免这种情况:第一种方法是使用eval
,但这很危险并且有许多警告:用
#!/bin/bash
eval "$1 sleep 3"
使用此脚本:
$ ./script "echo hello;"
hello
$
(延迟3秒后显示最终提示)。 ./script "echo hello &&"
也是如此。 但这不是一个好主意。请继续阅读。
作为一般规则,你应该避免(至少在Bash中)混合数据和代码:变量在这里保存数据(字符串),而不是代码。所以这种技术不是很好。我说这不是一个好的,不仅因为有人曾经说过你不应该将数据与代码混合在一起,而是因为如果你开始在你的“代码”中使用特殊字符,它就会变得无法使用。例如,如果您想回显*
符号,则必须使用笨重的调用./script "echo \"*\";"
。还有许多其他警告。
代码保存在函数或脚本中。使用函数:
say_hello_and_execute() {
echo hello; "$@"
}
然后导出此函数:export -f say_hello_and_execute
并将其用作脚本的参数:./script say_hello_and_execute
。
使用脚本可以实现同样的目的:调用以下脚本say_hello_and_execute
:
#!/bin/bash
echo hello; "$@"
然后chmod +x say_hello_and_execute
并以./script ./say_hello_and_execute
运行您的脚本。
这个答案试图回答你的问题。但是,如果你能准确地告诉我们你想要实现的目标会更好,因为可能有更好的设计来实现这一目标。