脚本写了 #!/bin/bash,运行仍报 Bad substitution:检查是否用 sh 启动
Bash脚本首行正确仍报Bad substitution时,检查是否由sh读取,修正真实任务入口并保留合适执行权限。
脚本首行是 #!/bin/bash,服务器也安装了 Bash,但 ${text/Linux/Unix} 这样的替换仍报 Bad substitution。若启动命令是 sh script.sh,首先修正调用方式:这会让 sh 直接读取文件,首行不会替它切换到 Bash。
本文适用于使用 Bash 参数替换的 Linux 脚本,尤其是 /bin/sh 由 dash 等其他 shell 提供的系统。错误文本本身只能帮助定位,不能独自证明实际解释器。
对照脚本内容和启动入口
下面是仅用于说明的脚本:
#!/bin/bash
text='I love Linux'
printf '%s\n' "${text/Linux/Unix}"
/ 形式的字符串替换是 Bash 支持的参数展开,不能假设所有 sh 都支持。检查实际运行入口,包括手工命令、cron 条目、systemd unit、CI 脚本和外层脚本中的调用。最容易漏掉的是外层明确写了 sh /path/to/script.sh。
不要仅用 bash --version 或交互终端中的 $SHELL 判断。前者表示某个 Bash 可用,后者通常描述登录 shell;两者都不能说明这一轮脚本由谁解析。
让调用方式与语言一致
如果脚本确实依赖 Bash,使用明确解释器:
/bin/bash /path/to/script.sh
/bin/bash 和脚本路径是示例,要核对目标主机的真实路径。这种调用不要求脚本本身有执行位,但执行用户必须能读取脚本并访问各级目录。
也可以把脚本作为独立程序启动,让系统使用第一行指定的解释器:
chmod u+x /path/to/script.sh
/path/to/script.sh
第二种方式需要有效 shebang、执行权限及允许执行的挂载设置。chmod u+x 只增加文件所有者的执行权限,不会给其他用户授权;任务若由别的账号运行,要按原权限策略设置对应的最小权限。
修改 cron、unit 或 CI 的入口时保留原配置;脚本若会写数据,先在读者自己的验收环境确认行为再恢复定时执行,避免反复手工触发产生重复业务操作。修改 systemd unit 后还需按管理范围重新加载。
必须保留 sh,就改掉 Bash 专用语法
目标运行环境只有 POSIX sh 时,应检查整个脚本并改写 Bash 专用部分,不只是把首行改成 #!/bin/sh。数组、[[ … ]]、某些参数替换等都可能继续出错。替换字符串的方法要按数据是否含换行、特殊字符和匹配模式选择,不能随便换成一条 sed 就宣称语义相同。
不要将系统 /bin/sh 改为指向 Bash 来修这一个脚本,那会影响其他程序约定的解释器行为。
调用已正确但仍报错
若实际已由 Bash 解析,继续检查报错行是否存在 ${}、括号未闭合、拼写错误或只在较新版本支持的语法。本文中的基础字符串替换在 Bash 4.x 可用;无须因这个表达式直接升级系统 shell。
成功标准是通过真实的任务入口执行时不再出现参数展开错误,替换结果符合预期,后续任务也正常完成。终端手工调用正确、定时入口仍写着 sh,问题还没有解决。


