文章目录
- 1. 问题产生的背景
- 2. 排查
- 3. 问题产生的原因
- 4. 解决方法
先说结论:这个报错的真实含义是
找不到脚本解释器,而不是找不到脚本文件本身!!!
1. 问题产生的背景
我的初衷是将jar包打成docker镜像并部署在Linux上(感兴趣的可以看一下我的另一篇文章:将jar包打成docker镜像并部署在Linux上)
刚开始一切都很顺利,但在启动容器时却报了以下错误
exec /tmp/start.sh: no such file or directory
Dockerfile
文件和 start.sh
文件都存放在 /tmp/docker
目录下
Dockerfile文件的内容如下
# 指定基础镜像
FROM java:8-alpine
# 复制文件到镜像中
COPY ./docker-demo-0.0.1-SNAPSHOT.jar /tmp/docker-demo-0.0.1-SNAPSHOT.jar
COPY ./start.sh /tmp/start.sh
# 暴露端口
EXPOSE 16256
# 赋予脚本文件执行权限
RUN chmod +x /tmp/start.sh
# 指定容器启动时运行的指令
ENTRYPOINT ["/tmp/start.sh"]
start.sh脚本文件的内容如下
#!/bin/bash
# 第一个命令
java -jar /tmp/docker-demo-0.0.1-SNAPSHOT.jar
# 第二个命令(如果需要的话)
echo "Hello, World"
2. 排查
我在网络上搜索了各种解决方法,都没有解决问题,我甚至还多次怀疑过Dockerfile文件中的文件复制语句是不是写错了、Dockerfile文件是不是不能使用多条COPY指令,在这个方向上浪费了不少时间。。。
直到我看到这篇文章才有了明确的方向:容器中sh脚本明明存在,为何会报"no such file or directory"的错误?
按照上述文章的方法,我开始逐步排查问题
- 我将
Dockerfile
文件中的ENTRYPOINT ["/tmp/start.sh"]
改成了ENTRYPOINT ["java", "-jar", "/tmp/docker-demo-0.0.1-SNAPSHOT.jar"]
,先将容器启动起来 - 容器成功启动,说明Dockerfile文件中的文件复制语句没问题
- 接着进入容器内部,在容器内手动执行 ./start.sh,看到报错信息还是
./start.sh: not found
- 但我通过ls命令确实可以查看到start.sh脚本文件的存在
- 接着我通过cat命令查看start.sh脚本文件的内容,结果如下
- 不看不知道,一看吓一跳,一堆乱码
- 但是,这并不是问题产生的根本原因,即使是乱码,也不影响脚本文件的执行,因为乱码都被注释掉了
- 我猛然想起,当时创建文件之后,为了方便,就没有使用Linux环境下的vim编辑器,而是通过FinalShell软件将脚本文件下载到本地,接着使用Windows环境下的NodePad++软件编辑脚本
- 我再次通过FinalShell软件将脚本文件下载到本地,查看文件的换行符和编码格式,发现文件的换行符竟然是Windows环境下的CRLF,编码竟然是GBK
- 因为我最开始用NodePad++编辑这个文件时,文件是空的,NodePad++应该是根据我当时的地理位置和电脑的操作系统自动帮我修改了换行符和文件的编码(真是血与泪的教训o(╥﹏╥)o,花了我将近两个小时)
- 当我修改了文件的换行符和文件的编码后,以为问题已经解决了,再次运行容器,没想到还是报以下错误
exec /tmp/start.sh: no such file or directory
o(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)o - 但是,已经有了排查经验的我,看到同样的报错,觉得肯定是脚本文件的第一行出了问题
- 我突然回想起,不同的Linux系统,默认使用的脚本解释器可能不一样(CentOS使用的是bash)
- 于是我再次进入容器内部,查看容器内部默认使用的脚本解释器
- 没想到,容器内部使用的脚本解释器竟然是ash
- 将start.sh文件的第一行改成
#!/bin/ash
后,重新构建容器并运行,终于成功了o(╥﹏╥)oo(╥﹏╥)oo(╥﹏╥)o - 至此,问题终于解决(总耗时将近3个小时)
3. 问题产生的原因
- 在不同的操作系统上,换行符有所差别
- Windows环境下的换行符是CRLF
- Linux环境下的换行符是LF
- 不同的Linux系统默认使用的脚本解释器可能不同
当这个使用了CRLF换行符(Windows环境下的换行符)的脚本文件进入到Docker容器中,第一行就变成了
#!/bin/bash[看不见的CRLF]
而在Linux系统上执行脚本时,LF会被认为是Linux换行符,那么解释器的名称就变成了 bin/bashCR
,系统肯定找不到这个文件,所以就会报错 no such file or directory
综上所述,这个报错的真实含义是找不到脚本解释器,而不是找不到脚本文件本身!!!
4. 解决方法
- 删除脚本文件,用Linux的vim编辑器重新编写脚本,重新构建容器
- 将脚本文件的第一行换成
#!/bash/容器的脚本解释器
- 如果你确实想用Windows环境下的文本编辑器,建议先在脚本文件中编写一些内容,接着通过FinalShell等软件将脚本下载到本地,再用Windows环境下的文本编辑器修改脚本,同时要确保文件的换行符为UNIX环境下的LF,编码使用的是UTF-8!!!
- 脚本文件的第一行可以统一使用
#!/bash/sh
,因为Linux系统一般都会有一个sh软连接,这个软连接指向的就是真正的脚本解释器