Bash 从命令行传递字符串时的怪癖
Bash quirk when passing string from command line
这是我为演示古怪行为而创建的示例。
我希望 bash 按原样传递引用的命令行参数。
john@doe:~/tmp$ cat script.sh
#! /bin/bash
set -o xtrace
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 规则是什么以及如何规避它?非常感谢:)
规则是引用否则会有不同解释的元字符。分号是语句分隔符,&&
也是如此。引用使得剪切和粘贴特定行变得容易 并让它重现原始效果 .
想想
的区别
echo hello '&&'
对比
echo hello &&
第一个回显两个词,第二个等待更多输入。你称之为古怪的引述按设计工作,是你真正想要的,不,除了破解 bash 源代码之外别无他法。
手册中概述了该规则:Section Shell Operation。
您将阅读:
The following is a brief description of the shell’s operation when it
reads and executes a command. Basically, the shell does the following:
- Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the
-c
invocation option (see Invoking
Bash), or from the user’s terminal.
- Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by
metacharacters. Alias expansion is performed by this step (see
Aliases).
- Parses the tokens into simple and compound commands (see Shell Commands).
- Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename
Expansion) and commands and arguments.
- Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
- Executes the command (see Executing Commands).
- Optionally waits for the command to complete and collects its exit status (see Exit Status).
所以,有了你的脚本:
#! /bin/bash
sleep 3
当你执行 ./script "echo hello ;"
时会发生什么?
- Bash 阅读您的脚本。
- 它将输入分解为单词和运算符。请注意,此时 Bash 没有看到您的参数
echo hello ;
中的控制运算符 ;
。
此时,它只看到三个标记 </code>、<code>sleep
和 3
.
- 它将标记解析为简单和复合命令。目前还没有简单或复合命令。
- 它执行 shell 和文件名扩展:此时,您的令牌
</code> 被扩展为 <code>echo
、hello
和 ;
(不是这里的分号只是一个参数,它不能被理解为控制运算符,因为控制运算符的扫描已经在步骤2中完成,不会再执行)。
- 这里没有要执行的重定向。
- Bash 现在执行命令:它看到带有参数
hello
、;
、sleep
和 3
的命令 echo
。所以它愉快地回应了hello ; sleep 3
.
- 在执行
echo
后收集退出状态(很可能成功)。
有两种方法可以避免这种情况:第一种是使用 eval
,但这很危险并且有很多注意事项:将您的脚本替换为
#!/bin/bash
eval " 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
。
此答案试图回答您的问题。不过,如果您能准确地告诉我们您想要实现的目标会更好,因为可能会有更好的设计来实现它。
这是我为演示古怪行为而创建的示例。 我希望 bash 按原样传递引用的命令行参数。
john@doe:~/tmp$ cat script.sh
#! /bin/bash
set -o xtrace
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 规则是什么以及如何规避它?非常感谢:)
规则是引用否则会有不同解释的元字符。分号是语句分隔符,&&
也是如此。引用使得剪切和粘贴特定行变得容易 并让它重现原始效果 .
想想
的区别echo hello '&&'
对比
echo hello &&
第一个回显两个词,第二个等待更多输入。你称之为古怪的引述按设计工作,是你真正想要的,不,除了破解 bash 源代码之外别无他法。
手册中概述了该规则:Section Shell Operation。
您将阅读:
The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:
- Reads its input from a file (see Shell Scripts), from a string supplied as an argument to the
-c
invocation option (see Invoking Bash), or from the user’s terminal.- Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step (see Aliases).
- Parses the tokens into simple and compound commands (see Shell Commands).
- Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments.
- Performs any necessary redirections (see Redirections) and removes the redirection operators and their operands from the argument list.
- Executes the command (see Executing Commands).
- Optionally waits for the command to complete and collects its exit status (see Exit Status).
所以,有了你的脚本:
#! /bin/bash
sleep 3
当你执行 ./script "echo hello ;"
时会发生什么?
- Bash 阅读您的脚本。
- 它将输入分解为单词和运算符。请注意,此时 Bash 没有看到您的参数
echo hello ;
中的控制运算符;
。 此时,它只看到三个标记</code>、<code>sleep
和3
. - 它将标记解析为简单和复合命令。目前还没有简单或复合命令。
- 它执行 shell 和文件名扩展:此时,您的令牌
</code> 被扩展为 <code>echo
、hello
和;
(不是这里的分号只是一个参数,它不能被理解为控制运算符,因为控制运算符的扫描已经在步骤2中完成,不会再执行)。 - 这里没有要执行的重定向。
- Bash 现在执行命令:它看到带有参数
hello
、;
、sleep
和3
的命令echo
。所以它愉快地回应了hello ; sleep 3
. - 在执行
echo
后收集退出状态(很可能成功)。
有两种方法可以避免这种情况:第一种是使用 eval
,但这很危险并且有很多注意事项:将您的脚本替换为
#!/bin/bash
eval " 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
。
此答案试图回答您的问题。不过,如果您能准确地告诉我们您想要实现的目标会更好,因为可能会有更好的设计来实现它。