问题

问题描述:

由于云堡垒机在使用vim编辑shell脚本粘贴多行内容时会出现内容部分丢失,所以我先在Windows的记事本上编写了unzip_recursive.sh.txt,在上传到云堡垒机,去掉.txt 后缀执行脚本,发现报错。

1
2
3
4
5
6
7
[root@kf-zgttslyypt-tyszr-xzzs unzip-area]# ll
total 1470448
-rwxr-xr-x 1 root root 3623 Sep 23 17:40 unzip_recursive.sh
-rw-r--r-- 1 root root 1505730064 Sep 23 16:15 V1.1.5.2.zip
[root@kf-zgttslyypt-tyszr-xzzs unzip-area]# ./unzip_recursive.sh
-bash: ./unzip_recursive.sh: /bin/bash^M: bad interpreter: No such file or directory
[root@kf-zgttslyypt-tyszr-xzzs unzip-area]#

报错原因:

这个错误是因为脚本文件的行尾符(line endings)问题。在 Windows 或类 Windows 系统中编辑了脚本,导致文件包含了 CRLF\r\n)行尾符,而 Linux 系统只需要 LF\n)行尾符。

错误信息中的 ^M 就是 CR(Carriage Return)字符在 Linux 中的显示。

解决方案:

使用 sed 命令删除 CR 字符

1
sed -i 's/\r$//' unzip_recursive.sh

预防措施:

为了避免将来再次出现这个问题:

  1. 在 Linux 中编辑脚本:使用 vimnano 等 Linux 原生编辑器
  2. 如果必须在 Windows 中编辑
    • 使用 VS Code,并在右下角状态栏选择 “LF” 而不是 “CRLF”
    • 使用 Notepad++,设置:编辑 → 文档格式转换 → 转换为 Unix 格式
  3. Git 配置:如果你使用 Git,可以设置:
1
git config --global core.autocrlf input

相关知识

行尾符

行尾符,也叫做换行符,是嵌入在文本文件中的不可见字符,它的作用是告诉计算机“一行文本在这里结束,新的一行从这里开始”。

你可以把它想象成写作时的“回车换行”操作:在纸上写完一行字后,你把笔移回本行的开头(回车),然后向下移动一行(换行)。

不同的操作系统在发展过程中选择了不同的字符来表示行尾符:

操作系统 行尾符符号 转义序列 说明
Unix / Linux / macOS (现代) LF (Line Feed) \n 仅使用“换行”一个字符
Windows / DOS CRLF (Carriage Return + Line Feed) \r\n 使用“回车”+“换行”两个字符
Classic Mac OS (旧版本) CR (Carriage Return) \r 仅使用“回车”一个字符

历史原因

  • 在古老的打字机/电传打字机上,“回车”和“换行”确实是两个独立的机械动作。

  • Windows 继承了 DOS 的传统,使用了两个字符(CRLF)来完成这一操作。

  • Unix 设计者认为一个字符(LF)就足够了,更简洁。

让我们看看你遇到的错误信息:

1
-bash: ./unzip_recursive.sh: /bin/bash^M: bad interpreter: No such file or directory

这里的 ^M 就是问题的关键!

  • 在Linux的显示中,\r(Carriage Return)字符会被显示为 ^M
  • 所以,脚本的第一行 #!/bin/bash 在Linux看来实际上是 #!/bin/bash\r
  • 当Bash尝试执行时,它寻找的解释器路径就变成了 “/bin/bash\r”,而不是 “/bin/bash”
  • 系统中当然不存在一个叫 bash^M 的程序,所以就会报错 “No such file or directory”。