Linux脚本无法执行:换行符、解释器和权限

脚本从电脑上传到服务器后跑不起来,先保留完整报错,不要一上来就给所有文件加执行权限。文件存在不代表解释器存在;脚本能被读取,也不代表它能由当前 Shell 正确执行。

适用范围:Linux 上来源可信、内容可检查的 Shell 文本脚本。下文使用 GNU 工具,以当前目录的 job.sh 为例。未知来源脚本应先审查内容,排错时不要用提权运行来碰运气。

先判断报错发生在哪里

现象 优先检查
bad interpreter、解释器路径带 ^M 首行指定的解释器及 CRLF 换行
文件存在,却报 No such file or directory 解释器路径是否真实存在,是否夹有回车字符
command not found 报错对应哪一行;命令名称、PATH 或行尾回车
Permission denied 文件权限、父目录可遍历性、挂载和安全策略
unexpected token、语法错误 实际使用的 Shell 是否支持脚本语法
file -- ./job.sh
sed -n '1,5l' -- ./job.sh
command -v bash
ls -l -- ./job.sh

file 用于辅助判断文件类型;sed 把前五行中的不可见字符显示出来。如果每行结尾出现 \r$,通常是 Windows 风格的 CRLF。首行若成为 #!/bin/bash\r,系统寻找的是带回车字符的路径,补一次 chmod 解决不了。

换行符修复放到副本上做

先确认它确实是文本脚本。不要对整个目录批量去回车,更不要转换压缩包、程序文件等二进制内容。下面新建临时目录,复制时保留权限位和时间戳,再只删除副本的行尾回车;编辑后修改时间自然会变化。命令用 && 连接,前一步失败便停止后续操作。

fixdir=$(mktemp -d ./script-check.XXXXXX) &&
cp --preserve=mode,timestamps -- ./job.sh "$fixdir/job.sh" &&
sed -i 's/\r$//' "$fixdir/job.sh" &&
bash -n -- "$fixdir/job.sh"

bash -n 只检查 Bash 语法,不执行脚本业务。无输出且退出码为 0,说明语法检查通过,并不证明路径、依赖或业务逻辑正确。如果脚本本来由其他解释器执行,应使用该解释器提供的检查方式。原文件没有修改,发现处理不合适时直接停止使用副本即可。

确认“谁来执行”及“执行什么”

直接运行 ./job.sh 时,首行 #! 指定解释器;而 sh job.sh 明确交给 sh,不会因首行写了 Bash 就自动换回 Bash。不同系统的 sh 可能指向不同程序,数组等 Bash 语法不能据此认定可用。用 command -v bash 确认实际路径,再核对首行;使用 /usr/bin/env bash 还依赖运行环境的 PATH。

若错误来自脚本中间某一行,查那一行调用的命令是否存在。输入 job.sh 会按命令搜索规则查找;运行当前目录文件通常写 ./job.sh。不要为此把不可信目录加入全局 PATH。终端能运行而定时任务失败时,还应核对工作目录及环境变量。

准备查看错误行时,先在本机阅读,不要把完整脚本直接发到公开讨论区。脚本可能写着密码、访问令牌或内部地址;调试输出也可能展开这些参数。只保留报错、相关调用和脱敏后的首行,一般就能判断下一步该查解释器还是脚本内部命令。

最后处理权限并验证

只有确认运行账户缺少必要权限,才按需调整。例如文件由当前用户拥有,且准备直接执行时,可只给副本增加所有者执行位。父目录缺少遍历权限、文件系统使用 noexec 或安全策略拒绝访问,需要分别查证;不要改成全员可写,也不要通过换一种调用方式绕过策略。

确认副本内容后,在隔离环境用无副作用的输入验证。依赖脚本所在目录的程序,要特别核对副本位置改变的影响。正式替换前保存原文件及权限,验证失败就恢复这一份;不能只凭语法检查成功覆盖线上脚本。

延伸阅读