为何Dash在脚本与交互式环境下的引号处理行为不同?
问题:Dash脚本与交互式命令行的引号处理差异
我不想纠结shell扩展的复杂细节,但发现Dash在交互式命令行和脚本里的引号处理逻辑不一样。我写了个调用start-stop-daemon的脚本自动启动mjpeg-streamer,这个程序要求部分选项带空格,得用引号包裹。脚本的shebang加了-x参数,能看到start-stop-daemon的调用里引号用得没问题,但mjpeg-streamer就是跑不起来。可把同一行命令粘到交互式命令行里,却能正常运行。我想搞懂原理,就写了三个简化脚本隔离问题。
脚本1:模拟mjpeg-streamer的参数打印程序
$ cat to_be_daemon #!/bin/sh echo "num=$#" while [ -n "$1" ]; do echo "opt: $1" shift done exit 0
脚本2:模拟start-stop-daemon的调用逻辑
$ cat ssd_mimic #!/bin/sh exe="$1" shift ${exe} "$@" exit 0
脚本3:简化版启动脚本
$ cat starter #!/bin/sh -x exe="/tmp/to_be_daemon" input="input -r 1920x1080 -f 30" output="output -n -p 8080" options="-i '${input}' -o '${output}'" /tmp/ssd_mimic ${exe} -- ${options} exit 0
运行starter脚本的输出
$ ./starter + exe=/tmp/to_be_daemon + input=input -r 1920x1080 -f 30 + output=output -n -p 8080 + options=-i 'input -r 1920x1080 -f 30' -o 'output -n -p 8080' + /tmp/ssd_mimic /tmp/to_be_daemon -- -i 'input -r 1920x1080 -f 30' -o 'output -n -p 8080' num=12 opt: -- opt: -i opt: 'input opt: -r opt: 1920x1080 opt: -f opt: 30' opt: -o opt: 'output opt: -n opt: -p opt: 8080' + exit 0
可以看到,引号被当作普通字符处理,没有起到合并空格参数的作用。
交互式命令行运行结果
$ /tmp/ssd_mimic /tmp/to_be_daemon -- -i 'input -r 1920x1080 -f 30' -o 'output -n -p 8080' num=5 opt: -- opt: -i opt: input -r 1920x1080 -f 30 opt: -o opt: output -n -p 8080
此时引号正常工作,带空格的内容被识别为单个参数。
问题根源
核心原因是shell的引号解析时机不同:
- 交互式命令行中,输入命令时shell会先解析引号,把
'input -r 1920x1080 -f 30'识别成单个参数,引号本身会被shell丢弃,不会传递给后续程序。 - 脚本中,当你把引号写到变量
options里时,变量存储的是包含引号字符的字符串。执行/tmp/ssd_mimic ${exe} -- ${options}时,shell会先做单词拆分,把${options}按空格拆分成多个部分,这里的引号只是字符串里的普通字符,不会触发shell的引号解析逻辑,所以'input、-r等会被当成独立参数传递。
另外,用bash情况更糟,是因为bash的变量展开和单词拆分逻辑与Dash存在差异,但本质问题还是变量里的引号不会被二次解析。
内容的提问来源于stack exchange,提问作者Peter Rottengatter
相关产品推荐
相关产品推荐

