如何确认Bash通配符扩展时空白符与特殊字符是否已转义
问题1解答
你的推测不完全正确,通配符(路径名扩展)的结果并没有被转义,而是Shell的扩展执行顺序决定了这些结果不会再被分词处理:
Shell的内置扩展执行顺序为:
- 大括号扩展
- 波浪号扩展
- 参数/变量扩展、算术扩展、命令替换(从左到右执行)
- 字段拆分(即按
$IFS对前面步骤的输出做拆分) - 路径名扩展(也就是通配符匹配展开)
你第一个示例中for file in *.txt的*.txt是在字段拆分之后才完成展开的,每个匹配到的文件名直接作为独立的循环项,不会再经过$IFS拆分步骤,所以就算文件名带空格也不会被拆成多个项。
而第二个示例中,你先通过files=$(ls *.txt)做了命令替换,这个步骤是在字段拆分之前执行的,ls输出的带空格的文件名会被当成普通字符串,后续经过字段拆分步骤时就会按空格拆成多个独立项,才出现了你看到的拆分结果。
问题2解答
相关规则明确记载在GNU Bash官方手册的「Shell Expansions」章节,你可以在本地通过man bash命令搜索「EXPANSION」部分查看完整说明,核心规则为:字段拆分仅作用于参数扩展、命令替换、算术扩展的未加引号的结果,路径名扩展的结果不会再次拆分。
问题3解答
可以通过以下两种方式直观观测扩展过程:
- 用
printf打印每个独立参数
路径名展开的每个结果都是独立的参数,用printf可以逐个打印,能清晰看到每个文件名是完整的单个参数:
touch file\ {1..2}.txt printf "项: %s\n" *.txt
输出结果会直接显示每个文件名是完整的独立项:
项: file 1.txt 项: file 2.txt
如果是命令替换的结果,就会被拆分:
files=$(ls *.txt) printf "项: %s\n" $files
输出会显示空格拆分后的结果。
- 结合数组和
declare -p查看扩展结果
把通配符展开结果存到数组里,用declare -p可以直接打印数组的完整结构,能直观看到每个元素是完整的文件名:
arr=(*.txt) declare -p arr
输出类似:
declare -a arr=([0]="file 1.txt" [1]="file 2.txt")
如果是把未加引号的命令替换结果存到数组,就会看到拆分后的多个元素:
arr=($(ls *.txt)) declare -p arr
输出为:
declare -a arr=([0]="file" [1]="1.txt" [2]="file" [3]="2.txt")
你提到的set -x也可以观测,执行set -x后再运行for file in *.txt; do :; done,可以看到展开后的每个文件名都是作为独立的循环项存在,没有额外转义字符。
内容的提问来源于stack exchange,提问作者Dave Kerr
相关产品推荐
相关产品推荐

