故障现象
最近同事遇到一个问题,他的一段代码使用 system()
执行文件系统中的一个 shell 脚本时提示 not found 错误,简单记录帮他定位解决的过程。
为简化问题,假设需要执行的脚本文件 echo.sh
在根目录中,内容如下:
#!/bin/sh
echo $PATH
功能很简单,即打印环境变量 $PATH
的值。同事的 C 代码如下:
system("/echo.sh");
执行此语句时,只提示 not found,而没有提示具体什么找不到,需要依次排查几个可疑点。
故障排查
首先可以排除的是找不到脚本文件 echo.sh
本身。因为调用时使用的是绝对路径,且 ls
命令显示脚本文件确实存在,且有可执行权限。
# ls -l /echo.sh
-rwxr-xr-x 1 root root 21 Nov 2 2018 /echo.sh
由于脚本文件首行指定将 /bin/sh
作为解析器,因此接着排查 /bin/sh
是否指向了正确的 shell。
# ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Oct 30 2018 /bin/sh -> ash
# ls -l /bin/ash
-rwxrwxrwx 1 root root 7 Oct 30 2018 /bin/ash -> busybox
# ls -l /bin/busybox
-rwxrwxrwx 1 root root 2433952 Oct 30 2018 /bin/busybox
进一步验证 shell 没有问题,在 C 代码中显式地使用 sh
执行脚本正常:
system("sh /echo.sh");
为了加速问题定位,直接尝试在系统 shell 下执行 /echo.sh
,问题依旧。
之后查看文件内容,使用系统 busybox 集成的功能极其简单的 vi
命令打开脚本文件,注意首行最后的 ^M
:
#!/bin/sh^M
echo $PATH
由此怀疑是行结束符导致的找不到解析器,使用 busybox 集成的 dos2unix
命令将脚本文件转换成 Unix 风格的行结束符:
# dos2unix /echo.sh
之后再执行就正常了,问题解决。
# /echo.sh
/sbin:/bin:/usr/sbin:/usr/bin:/sbin
其实同样的脚本在较新版本的 bash 中执行时可明确给出错误的原因:
# /echo.sh
sh: /echo.sh: /bin/sh^M: bad interpreter: No such file or directory
因为出现问题的系统中使用的 busybox 版本较老,提示信息不完善,导致上述的几个排查步骤。
查看行结束符
vim
打开文件后,可使用 :set list
和 :e ++ff=unix
显示行结束符,$
表示换行符(ASCII 码 0x0A
),^M
表示回车符(ASCII 码 0x0D
)。Unix 风格仅有 $
,DOS 风格同时有 ^M
和 $
。
file
命令查看文件类型时,若为 DOS 风格行结束符,会有 with CRLF line terminators
字样。
# file deploy.sh
deploy.sh: POSIX shell script, ASCII text executable
# file echo.sh
echo.sh: POSIX shell script, ASCII text executable, with CRLF line terminators
小结
- 交叉编译环境需要特别注意脚本文件的行结束符,将 Windows 下编写的脚本文件部署到 Unix 系统环境之前,最好使用
dos2unix
命令转换,反方向的部署请使用unix2dos
命令。 - 可使用
vim
或file
查看文件的行结束符。
以上。