我正在探索使用set -x( set +xto unset) in跟踪命令bash:
在展开之后和执行之前,打印简单命令的踪迹,用于命令、case 命令、选择命令和命令及其参数的算术或关联的单词列表。PS4 变量的值被扩展,结果值打印在命令及其扩展参数之前。
现在考虑以下内容,跟踪echo \[-neE\] \[arg …\]带和不带引号的 bash 内置命令的使用:
# set -x # what I typed
# echo 'love' # ...
+ echo love <--(1) the trace
love # the output
# echo love? # note the input contains no quote whatsoever
+ echo 'love?' <--(2) note the trace contains quotes after returning word
love? # i.e. failed to find any file
# echo 'love?' # note the input contains single quotes
+ echo 'love?' <--(3) traces like (2)
love?
# touch loveu # we create a file that matches the love? pattern
+ touch loveu
# echo love? # of course now, the pattern matches the created file now
+ echo loveu <--(4) indeed it finds it and expands to name
loveu # the name is echoed
Run Code Online (Sandbox Code Playgroud)
所以?在这种情况下确实被解释为一个特殊字符,用于模式匹配路径名扩展中的单个字符。果然,一旦在当前目录中创建了与模式匹配的文件,就会发生匹配并打印文件名。当然,这种行为被记录在案:
如果没有找到匹配的文件名,并且 shell 选项 nullglob 被禁用,则该词保持不变。
但问题是 (2) 中的单词love?没有被引用not 'love?'。跟踪显示了命令执行之前但扩展之后的状态,正如我们所看到的,存在路径名扩展,因为?在第一个 case(2) 中没有匹配项,我们使用了特殊字符。那么在这种情况下会出现单引号,就像我们自己使用单引号(3) 和相同的字符串一样?而在其他情况下,要么找到了文字,要么找到了匹配项,并相应地“替换”了命令中的模式。这似乎是扩展后立即删除报价的手册部分的含义:
在前面的扩展之后,所有不是由上述扩展之一产生的字符 '\'、''' 和 '"' 的所有未加引号的出现都被删除。(我的斜体)
所以在这里(2)我们有未引用的事件,'这些事件是由先前的扩展引起的。我没有把它们放在那里;bash 做了,现在它们没有被删除- 我们就在命令执行之前。
类似的插图 for
考虑在for name [ [in [words …] ] ; ] do commands; done循环1中使用的这个列表,没有匹配的文件:
# for i in love love? 'love?'; do echo $i; done
+ for i in love 'love?' ''\''love?'\'''
+ echo love
love
+ for i in love 'love?' ''\''love?'\'''
+ echo 'love?'
love?
+ for i in love 'love?' ''\''love?'\'''
+ echo 'love?'
love?
Run Code Online (Sandbox Code Playgroud)
所以echo命令行为是完全相同的,但在结构中的项目的情况下for,它似乎试图......逃避自己引用我的引号??我不确定...
问题
single quotes在上下文中表示为(2);无论如何,扩展已经完成,我们要执行吗?同样,我们已经完成了扩展并且模式失败了 - 没有什么应该再扩展了。我想我要问的是为什么我们在这一点上关心 - 我们现在的重点是 bash 手册中的 3.7.2-4 之前。为什么这不是“按原样”保留并且扩展只是为了命令执行而关闭,即类似的东西set -f?for循环对列表中的单引号项做了什么?)1. 将这样的单词列表结构与 for 一起使用时,它实际上是一个项目列表,并且这些值确实是为了方便起见,因为我觉得 t="0"; for i in 0 0 0 0; do let t++; echo "yes, this is really $t times"; done 非常有说服力。
Mar*_*ick 12
当指示回声命令执行时为它们(“执行跟踪”),都bash和ksh周围的任何文字与元字符(加单引号*,?,;在里面,等)。
元字符可以通过多种方式进入这个词。单词(或其中的一部分)可以用单引号或双引号引起来,字符可以用 转义\,或者由于文件名匹配尝试失败而保留下来。在所有情况下,执行跟踪都将包含单引号词,例如:
$ set -x
$ echo foo\;bar
+ echo 'foo;bar'
Run Code Online (Sandbox Code Playgroud)
这只是 shell 实现执行跟踪方式的一个工件;它不会改变参数最终传递给命令的方式。添加、打印和丢弃引号。这是bash源代码的相关部分,print_cmd.c:
/* A function to print the words of a simple command when set -x is on. */
void
xtrace_print_word_list (list, xtflags)
...
{
...
for (w = list; w; w = w->next)
{
t = w->word->word;
...
else if (sh_contains_shell_metas (t))
{
x = sh_single_quote (t);
fprintf (xtrace_fp, "%s%s", x, w->next ? " " : "");
free (x);
}
Run Code Online (Sandbox Code Playgroud)
至于作者为什么选择这样做,那里的代码没有说。但是这里有一些类似的代码variables.c,并且带有注释:
/* Print the value cell of VAR, a shell variable. Do not print
the name, nor leading/trailing newline. If QUOTE is non-zero,
and the value contains shell metacharacters, quote the value
in such a way that it can be read back in. */
void
print_var_value (var, quote)
...
{
...
else if (quote && sh_contains_shell_metas (value_cell (var)))
{
t = sh_single_quote (value_cell (var));
printf ("%s", t);
free (t);
}
Run Code Online (Sandbox Code Playgroud)
所以可能这样做是为了更容易从执行跟踪的输出中复制命令行并再次运行它们。