如何在Bash脚本中无需引号传递含多空格的文件名?
很遗憾,直接通过./script1 file in 1这种无引号的方式让脚本接收完整的file in 1作为单个参数,在Bash里是做不到的,这得从shell的命令行解析机制说起:
当你在终端输入这条命令时,Bash会先执行**分词(Word Splitting)**操作:它会把连续的空白字符(包括多个空格、Tab、换行符)都当作参数分隔符,不管你输入多少个空格,都会被合并成一个分隔符,最终将命令拆分成三个独立的参数:file、in、1。等这些参数传递到你的脚本里时,原始的多空格已经完全丢失了——不管你用$*还是"$@",拿到的都是拆分后的三个参数,根本没法恢复原来的多空格。
可行的替代方案
既然绕不开shell的分词规则,我们只能通过改变输入方式来解决:
使用双引号包裹文件名(这是最标准的做法)
./script1 "file in 1"此时Bash会跳过对引号内内容的分词,直接把
file in 1作为单个参数传给脚本,脚本里用"$1"就能完整接收带多空格的文件名。转义每个空格
如果你不想用引号,可以在每个空格前加反斜杠,告诉Bash这是普通字符而非分隔符:./script1 file\ \ \ \ in\ 1这里的四个
\对应原来的四个空格,单个\对应后面的单个空格,同样能让Bash把整个内容当作单个参数。自定义分隔符(临时折中方案)
如果你实在不想手动加引号或转义,可以和脚本约定用特殊符号代替空格,比如用_表示单个空格,____表示四个空格,然后在脚本里替换回来:
输入时:./script1 file____in_1脚本里处理:
filename="$1" filename="${filename//____/ }" # 把____替换成四个空格 filename="${filename//_/ }" # 把_替换成单个空格 echo "Received filename: '$filename'"但这只是临时的 workaround,不如引号/转义直接可靠。
总结
shell的分词机制是命令行解析的基础规则,无引号情况下的多空格必然会被合并拆分,脚本无法恢复原始的空格数量。所以最稳妥的方式还是使用引号或转义来传递带特殊字符的参数。
内容的提问来源于stack exchange,提问作者Guy Ohayon

