3

文件格式引起的脚本执行错误 - Cocowool

 1 year ago
source link: https://www.cnblogs.com/cocowool/p/17124403.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

文件格式引起的脚本执行错误

当我们使用 Windows 桌面下的编辑器编写一个 Shell 文件时,很容易将文件使用的换行符保存为 dos 格式。如果将文件上传到 Linux 服务器执行时,可能会遇到下面的错误。这是因为

# 显示一个简单的shell文件
$ cat dosnewline.sh                                   
#!/bin/sh

echo "This is a file with dos newline"


# 该文件使用了 dos 格式的换行符
$ od -bc dosnewline.sh
0000000   043 041 057 142 151 156 057 163 150 015 012 015 012 145 143 150
           #   !   /   b   i   n   /   s   h  \r  \n  \r  \n   e   c   h
0000020   157 040 042 124 150 151 163 040 151 163 040 141 040 146 151 154
           o       "   T   h   i   s       i   s       a       f   i   l
0000040   145 040 167 151 164 150 040 144 157 163 040 156 145 167 154 151
           e       w   i   t   h       d   o   s       n   e   w   l   i
0000060   156 145 042 015 012 015 012 015 012                            
           n   e   "  \r  \n  \r  \n  \r  \n                            
0000071
# 使用 sh 执行的时候就会有一个报错
$ h dosnewline.sh    
: command not found 2: 
This is a file with dos newline
: command not found 4: 
: command not found 5: 
# 获取脚本的返回码也不是0,在一些自动化调用的场景中就会认为脚本执行失败,从而引发后续的问题
$ echo $?             
127
# 退出码 127 的意思是 command not foud,对应具体的 dos 换行符所在的行

我们通常所说的换行符在 ASCII 码表中对应下面两个字符。

十进制 十六进制 字符 编程时
10 A LF(Line feed,New Line) \n
13 D CR(Carriage return) \r

这两个字符被用作换行的标志,但是在不同操作系统中使用的不一样,具体如下:

操作系统 换行符
Unix(包括 Linux) \n
Windows \r\n
MacOS X 之前的版本 \r
MacOS X 及之后的版本 \n

为什么 Windows 中会用两个字符而其他系统使用一个字符呢?

据说很久以前,人们在使用老式电传打字机作为输入设备的年代,这种设备内部使用两个字符来另起新行。一个字符把滑动架移回首位 (称为回车),另一个字符把纸上移一行 (称为换行)。

当电子计算机问世后,由于存储器曾经非常昂贵。有些人认定没必要用两个字符来表示行尾。于是 UNIX 开发者决定他们可以用一个字符(LF)来表示行尾,Apple 开发者规定了用 (CR)来表示行尾,而 MS-DOS(以及后来的 Windows)开发人员则沿用了老式的两个字符 。

正是因为不同操作系统默认的换行符不同,导致在 Windows 下编写的文件采用了 Windows 下的换行符。而不幸的是 sh 做为 Linux 下的应用,只认识 Unix(包括 Linux)下的换行符,引发的文章开头的问题。

解决的方法有很多,从脚本来源上说,最好我们在编辑过程中就指定使用的换行符,大多数编码常用编辑器例如 Notepadd++ 等都支持这个选项,如下图在 Notepadd++ 的右下角会显示换行符的类型。千万不要使用 Windows 自带的记事本来编写 shell 脚本,记事本是不支持调整换行符的。

39469-20230215191756070-170551677.png

除了在编写阶段注意,脚本编写完成后,还可以通过 $ sh -x hello.sh 的方式来检查脚本是否有语法错误,对于本文提供的示例来说输出结果如下,可以看到输出结果给出提示多了 \r 的字符。

$ sh -x dosnewline.sh 
+ $'\r'
: command not found 2: 
' echo 'This is a file with dos newline
This is a file with dos newline
+ $'\r'
: command not found 4: 
+ $'\r'
: command not found 5: 

最后如果不小心,这样的脚本已经进入了生产环境,也还有很多的方法来进行修改。很多文章推荐使用 dos2unix 这个命令来快速修改,这个命令使用起来比较方便,但是对于一些生产环境管理严格的单位来说,这个命令未必允许在生产环境安装。

那就还可以用一般都有的 trawksed 命令来实现,下面给出具体示例。

$ tr -d '\r' < dosnewline.sh > dosnewline.sh-tr
# 使用 od 比较两个文件,后续的脚本可类似方式比较
$ od -bc dosnewline.sh-tr 
0000000   043 041 057 142 151 156 057 163 150 012 012 145 143 150 157 040
           #   !   /   b   i   n   /   s   h  \n  \n   e   c   h   o    
0000020   042 124 150 151 163 040 151 163 040 141 040 146 151 154 145 040
           "   T   h   i   s       i   s       a       f   i   l   e    
0000040   167 151 164 150 040 144 157 163 040 156 145 167 154 151 156 145
           w   i   t   h       d   o   s       n   e   w   l   i   n   e
0000060   042 012 012 012                                                
           "  \n  \n  \n                                                
0000064
$ od -bc dosnewline.sh   
0000000   043 041 057 142 151 156 057 163 150 015 012 015 012 145 143 150
           #   !   /   b   i   n   /   s   h  \r  \n  \r  \n   e   c   h
0000020   157 040 042 124 150 151 163 040 151 163 040 141 040 146 151 154
           o       "   T   h   i   s       i   s       a       f   i   l
0000040   145 040 167 151 164 150 040 144 157 163 040 156 145 167 154 151
           e       w   i   t   h       d   o   s       n   e   w   l   i
0000060   156 145 042 015 012 015 012 015 012                            
           n   e   "  \r  \n  \r  \n  \r  \n                            
0000071
$ awk '{ sub("\r$", ""); print }' dosnewline.sh > dosnewline.sh-awk 
$ sed 's/\r//' dosnewline.sh > dosnewline.sh-sed 

这篇文章首发在我的个人站点 大江小浪 上,更多内容,欢迎访问。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK