Bash脚本中FFmpeg复合命令变量执行失败,直接运行正常的原因?
问题根源与解决方案
这个问题的核心是Bash对变量展开时的引号解析规则在搞鬼,我来给你拆解清楚:
为什么脚本执行会报错?
当你把包含双引号的-filter_complex参数拼接到FFMPEG变量里,然后直接执行$FFMPEG时,Bash并不会把变量里的双引号当作语法层面的引号来处理——它会把这些双引号当成参数内容的一部分,原封不动地传递给ffmpeg。
这下ffmpeg收到的-filter_complex参数就变成了:
"nullsrc=size=640x240 [base]; [base][0:v] overlay [tmp1]; [tmp1][1:v] overlay=x=320"
注意开头的"是参数的一部分,ffmpeg会把"nullsrc当成一个过滤器的名字,自然找不到这个过滤器,所以抛出No such filter: '"nullsrc'的错误。
为什么直接复制命令执行没问题?
当你在命令行直接运行echo输出的内容时,Bash会先解析双引号:它会把双引号内部的整个字符串当作一个完整的参数,去掉外层双引号后再传递给ffmpeg。这时ffmpeg拿到的是正确的过滤器链内容,没有多余的引号,所以能正常工作。
解决方法
方案1:使用数组存储命令参数(推荐,最安全可靠)
Bash的数组可以完美保留每个参数的边界,不会破坏参数内部的空格和特殊字符。修改脚本如下:
#!/bin/bash #-_-_-_--- CONFIGURATION ---_-_-_- CAMERA_COUNT=2 SRC_WIDTH=320 SRC_HEIGHT=240 MARGIN=320 #-_-_-_--- END CONFIGURATION---_-_-_- CHARS=( {A..Z} ) FULL_WIDTH=$((SRC_WIDTH * CAMERA_COUNT)) # 用数组存储命令,每个参数作为数组的一个元素 FFMPEG=(ffmpeg) # 添加输入源参数 for ((i = 0; i < CAMERA_COUNT; i++)) do FFMPEG+=("-f" "flv" "-i" "rtmp://127.0.0.1:1935/live/PanoView${CHARS[i]}") done # 构建filter_complex的内容 filter_content="nullsrc=size=$((SRC_WIDTH * CAMERA_COUNT))x${SRC_HEIGHT} [base]; [base][0:v] overlay [tmp1];" for ((i = 1; i < CAMERA_COUNT - 1; i++)) do filter_content+="[tmp${i}][${i}:v] overlay=x=$((SRC_WIDTH * i)) [tmp$((i + 1))];" done filter_content+="[tmp$((CAMERA_COUNT - 1))][$((CAMERA_COUNT - 1)):v] overlay=x=$((SRC_WIDTH * (CAMERA_COUNT - 1)))" # 添加filter_complex和输出参数 FFMPEG+=("-filter_complex" "$filter_content") FFMPEG+=("-f" "flv" "-b:v" "1M" "-an" "-r" "25" "rtmp://127.0.0.1:1935/live/test") # 执行命令,注意数组展开的写法是"${FFMPEG[@]}" "${FFMPEG[@]}" # 打印命令可以用printf来正确展示参数边界 printf '%q ' "${FFMPEG[@]}" echo
这种写法能确保每个参数都被正确传递,完全避免引号解析的问题,也是处理复杂Bash命令的最佳实践。
方案2:使用eval(不推荐,有安全风险)
如果你只是临时解决问题,且能保证脚本里的变量都是可控的,可以把执行命令改成:
eval "$FFMPEG"
eval会让Bash重新解析变量里的内容,包括双引号,效果和直接在命令行执行一样。但要注意:如果CAMERA_COUNT或其他配置变量来自不可信输入,eval可能会执行恶意代码,所以尽量用数组方案。
内容的提问来源于stack exchange,提问作者CaptTaifun
相关产品推荐
相关产品推荐

