之前在执行脚本时发现一个问题:
cat a.sh
#!/bin/bash
OPTS="--conf javaOption=\"-Da=b -Dc=d\""
eval ./c.sh $OPTS
./c.sh $OPTS
cat c.sh
#!/bin/bash
n=$#
for ((i=0; i<n; i++));do
echo "$i:{"$1"}"
shift
done
对于加eval和不加eval处理的./c.sh $OPTS命令结果是不一样的,而且直接的./c.sh $OPTS得出的并不是我们想要的结果:
一、eval ./c.sh $OPTS
结果:
0:{–conf}
1:{javaOption=-Da=b -Dc=d}
二、./c.sh $OPTS
结果:
0:{–conf}
1:{javaOption=”-Da=b}
2:{-Dc=d”}
为了说明这个问题,我们分为两步:
(1)说明bash命令行处理流程;
(2)使用grep命令+log打印+gdb+xcode来从源码里解释流程。
首先来说一下bash命令行处理流程,分为12步,来自《学习bash》这本书中,流程图如下:
下边来举个例子,这是我在下载的bash源码中添加了printf输出后的一个简单命令执行的输出:
$ cat test1.sh
./c.sh –conf javaOption=”-Da=b -Dc=d”
$ ../bash-4.4/bash test1.sh
reader_loop
read_command
parse_command
yyparse
Starting parse
[Function]read_token
reserved_word_acceptable
CHECK_FOR_RESERVED_WORD
reserved_word_acceptable
reserved_word_acceptable
[Function]read_token
reserved_word_acceptable
CHECK_FOR_RESERVED_WORD
reserved_word_acceptable
reserved_word_acceptable
[Function]read_token
reserved_word_acceptable
CHECK_FOR_RESERVED_WORD
reserved_word_acceptable
reserved_word_acceptable
[Function]read_token
expand_words
brace_expansion //大括号扩展
shell_expand_word_list //5-8步
string_extract_double_quoted //双引号处理
-Da=b -Dc=d
dequote_string
-Da=b -Dc=d
quote_string
dequote_string
dequote_string
dequote_string
0:{–conf}
1:{javaOption=-Da=b -Dc=d}
上边的输出基本对应12步处理流程,下边来具体分析:
脚本内容:
OPTS=”–conf javaOption=\”-Da=b -Dc=d\””
./c.sh $OPTS
执行结果如下:
0:{–conf}
1:{javaOption=”-Da=b}
2:{-Dc=d”}
脚本内容:
./c.sh –conf javaOption=”-Da=b -Dc=d”
执行结果如下:
0:{–conf}
1:{javaOption=-Da=b -Dc=d}
这里第一个脚本未对双引号进行处理,为什么呢?
出现这种不同,主要是在于命令行处理流程中对于双引号的处理位置。在命令行处理流程中,位于subst.c文件9017(大约位置,因为加log信息后有变)处的函数 expand_word_internal()是处理从第五步波浪号扩展到第八步算术替换的核心函数,而实际对于双引号的处理是在与这几步变换并列的一个switch语句里,bash在处理完
相关的扩展之后会直接跳出switch,所以就跳过了关于双引号的处理,也就是说switch语句里case的顺序是
符号的相关扩展在前,双引号处理的在后。
在subst.c中添加的log信息如下:
对于./c.sh $OPTS的执行过程中打印:
[DEBUG] word: ./c.sh
[DEBUG] word: $OPTS
[DEBUG]$ begin substitute //在这里执行$OPTS的参数扩展
[DEBUG]$ end substitute
之后直接跳出switch循环了。
而对于./c.sh –conf javaOption=”-Da=b -Dc=d”的执行过程中打印:
[DEBUG] word: ./task/c.sh
[DEBUG] word: –conf
[DEBUG] word: javaOption=”-Da=b -Dc=d”
[DEBUG]double_quote processing //处理双引号
[DEBUG] word: -Da=b -Dc=d
Detaching after fork from child process 14855.
0:{–conf}
1:{javaOption=-Da=b -Dc=d}
在接下来的switch里边会处理到双引号,调用string_extract_double_quoted()函数将引号内字符串处理为一个整体。