在 Windows 电脑上编辑好 Shell 脚本,上传到 Linux 服务器后,文件明明存在,执行时却提示 bad interpreter;有时还会出现 ^M 或 $'\r': command not found。
这类问题可能与脚本的换行格式有关。
如果文件使用了 CRLF 换行符,行尾多出来的回车字符可能影响解释器路径或命令解析。下面介绍如何确认问题、备份脚本、转换换行符,并检查修复结果。
1. 为什么脚本上传后会出现换行符错误?
文本文件中的换行并不只有一种格式。
| 换行格式 | 字符表示 | 常见使用场景 |
|---|---|---|
| CRLF | \r\n | 部分 Windows 编辑器保存的文本 |
| LF | \n | Linux、Unix 环境中的文本 |
CRLF 比 LF 多了一个回车字符,也就是 \r。某些查看方式会把它显示为 ^M。
例如,脚本第一行原本是:
#!/bin/bash
当这行以 CRLF 结尾时,回车字符可能被包含在解释器路径中,导致系统无法找到对应的解释器。Linux 执行脚本时,脚本本身或指定的解释器不存在,都可能触发“没有那个文件或目录”一类错误。
常见报错可能包含:
/bin/bash^M: bad interpreter: No such file or directory
或者:
$'\r': command not found
具体提示取决于 Shell、脚本内容和启动方式。仅凭 No such file or directory,还不能直接认定是换行符问题。
另外,文件编码与换行格式是两回事:UTF-8 文件也可能使用 CRLF。

2. 操作环境与工具准备
本文以 Debian 12、Ubuntu 为例,处理对象是 ASCII 或 UTF-8 编码的 Bash 文本脚本。
如果文件被识别为二进制内容,或者使用 UTF-16 等其他编码,应先确认文件类型和编码,不要直接套用下面的转换步骤。
先安装检查工具:
apt-get update
apt-get install -y file dos2unix
以上安装命令使用 root 执行。如果使用具备 sudo 权限的普通账户,在命令前添加 sudo。
接下来,设置需要检查的脚本路径:
script_file='/opt/myjob/run.sh'
把 /opt/myjob/run.sh 替换为你的脚本实际绝对路径。后续命令沿用这个变量,请在同一个终端会话中操作。
检查和转换时,使用对脚本及其所在目录具有相应读写权限的账户。
如果需要独立的测试环境,可以使用莱卡云 Linux 云服务器进行操作。
查看官网购买链接: https://www.lcayun.com
9折优惠码:bloglcayun
3. 检查脚本是否包含 CRLF
3.1 查看文件信息
执行:
ls -l -- "$script_file"
file -- "$script_file"
对于使用 CRLF 的脚本,file 的输出可能包含:
ASCII text executable, with CRLF line terminators
其中,with CRLF line terminators 表示检测到了 CRLF 换行格式。
如果 ls -l 显示文件名后面带有 ->,说明当前路径是符号链接。先确认实际目标文件,再把 script_file 改为目标文件路径。
3.2 显示行尾的隐藏字符
执行:
LC_ALL=C sed -n '1,5l' "$script_file"
这里的 l 是小写字母 L,用于以可辨认的形式显示行内容及不可打印字符。
存在 CRLF 时,可能看到:
#!/bin/bash\r$
echo OK\r$
其中:
\r表示回车字符。$是这个查看命令显示的行尾标记,不是脚本中额外写入的字符。
这条命令只查看前五行。即使前五行正常,也不能排除后面的内容混用了其他换行格式。
3.3 检查整份文件
继续执行:
dos2unix -ih -- "$script_file"
这条命令只显示文件信息,不进行转换。输出示例:
DOS UNIX MAC BOM TXTBIN FILE
2 0 0 no_bom text /opt/myjob/run.sh
重点看:
- DOS: CRLF 换行数量。
- UNIX: LF 换行数量。
- MAC: 单独 CR 换行数量。
如果 DOS 大于 0,说明文件中包含 CRLF。如果 DOS 和 UNIX 都大于 0,说明文件混用了两种换行格式。
本文下面处理的是 CRLF。如果 MAC 列不为 0,或者文件编码不符合本文范围,建议先在编辑器中另存为 UTF-8 无 BOM、LF 换行,再继续检查。
4. 先备份,再转换为 LF
确认文件是需要处理的文本脚本后,执行:
backup_file="${script_file}.bak-$(date +%Y%m%d-%H%M%S-%N)"
cp -a -- "$script_file" "$backup_file" &&
dos2unix -- "$script_file"
这组命令会先在原目录生成带时间戳的备份,然后转换原脚本。
其中的 && 表示:只有备份命令成功,才继续执行转换。
dos2unix 默认采用原文件转换方式,所以备份应在转换前完成。
查看本次备份路径:
printf '备份文件:%s\n' "$backup_file"
如果转换提示失败或跳过文件,先处理对应原因,不要把“执行过命令”当作“转换成功”。
如需恢复本次备份,可以在同一个终端会话中执行:
cp -a -- "$backup_file" "$script_file"
这会用备份内容覆盖当前脚本。恢复前应确认,备份后没有需要保留的新修改。
5. 转换后如何确认修复成功?
5.1 重新检查换行格式
执行:
dos2unix -ih -- "$script_file"
LC_ALL=C sed -n '1,5l' "$script_file"
对于已经完成 CRLF 转 LF 的文件,应看到:
- DOS 列变成
0。 - 前五行的行尾不再出现
\r。
例如:
#!/bin/bash$
echo OK$
这里仍然显示的 $ 只是行尾标记,无需删除。
5.2 检查 Bash 语法
确认脚本使用 Bash 后,执行:
bash -n -- "$script_file" && printf 'Bash 语法检查通过\n'
bash -n 会读取并检查脚本语法,不执行其中的业务命令。语法检查通过,也不代表依赖程序、权限和运行环境都已满足。
5.3 按原来的方式验证运行
如果原来就是通过 Bash 手动执行,确认脚本内容和用途后,使用原业务账户、回到原工作目录运行:
bash -- "$script_file"
printf '退出状态:%s\n' "$?"
同时检查脚本日志和预期输出,确认任务实际完成。
如果原来通过 ./run.sh、Cron 或 systemd 启动,就应按原启动方式验证。直接执行 bash run.sh 成功,不能证明脚本首行的解释器设置和执行权限都正常。
最终以原启动方式下的执行结果为准,不要只看转换提示或语法检查。

6. 已经转成 LF,为什么还是报错?
换行符只是脚本无法运行的一种原因。如果确认 CRLF 已清除,可以继续按报错排查:
| 现象 | 继续检查的方向 |
|---|---|
仍然提示 bad interpreter | 脚本第一行指定的解释器路径是否正确,解释器是否安装 |
提示 Permission denied | 直接执行时的文件执行权限、父目录访问权限、挂载选项 |
bash run.sh 成功,直接执行脚本失败 | 脚本首行解释器声明和执行权限 |
| 手动执行成功,Cron 或 systemd 中失败 | 执行用户、工作目录、环境变量和命令路径 |
不要为了处理换行符错误,直接把文件权限改成 777。权限设置无法消除文件中的 CRLF。
如果问题出现在定时任务或服务启动过程中,可以继续参考本站文章:
7. 如何避免下次上传后再次报错?
7.1 在编辑器中统一保存格式
编辑 Linux Shell 脚本时,建议使用:
- 文件编码:UTF-8,无 BOM。
- 换行格式:LF。
修改后保存文件,再上传到服务器。如果只修复服务器上的文件,却没有修改电脑上的源文件,下次上传时仍可能把 CRLF 带回来。
7.2 使用 Git 时设置脚本换行规则
如果脚本保存在 Git 仓库中,可以在项目根目录的 .gitattributes 中加入:
*.sh text eol=lf
这条规则针对匹配的 .sh 文件启用文本规范化,并指定工作区使用 LF 换行。已有 .gitattributes 时,应补充规则,保留原来的其他配置。
需要注意:
- 这条规则不匹配没有
.sh扩展名的脚本。 - 添加规则不等于当前工作区中的所有旧文件都会立即被重写。
- 已有问题文件仍需修复,并检查 Git 差异后按项目流程提交。
8. 结语
Linux 脚本出现 ^M、bad interpreter 或 $'\r': command not found 时,可以先检查是否混入了 CRLF 换行符。
处理时按顺序完成:确认格式、备份文件、转换为 LF、检查语法、按原方式验证运行。
如果换行格式已经正常,就继续检查解释器路径、权限和运行环境。同时把电脑上的源文件保存为 UTF-8 无 BOM、LF 格式,可以减少重新上传后再次出现同类问题的情况。








