Are shell scripts sensitive to encoding and line endings?
我正在Mac上制作一个nw.js应用程序,想通过双击一个图标以dev模式运行该应用程序。第一步,我正在努力使我的shell脚本工作。
使用Windows上的vscode(我想获得时间),我在项目根目录下创建了一个
1 2 3 4 5 6 7 | #!/bin/bash cd"src" npm install cd .. ./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs"src" & |
但我得到了这个输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $ sh ./run-nw : command not found : No such file or directory : command not found : No such file or directory Usage: npm <command> where <command> is one of: (snip commands list) (snip npm help) npm@3.10.3 /usr/local/lib/node_modules/npm : command not found : No such file or directory : command not found |
我真的不明白:
- 它似乎以空行作为命令。在我的编辑器(vscode)中,我试图用
替换
造成问题),但没有改变。
- 似乎找不到文件夹(有或没有
dirname 指令),或者可能不知道cd 命令? - 似乎它不理解
install 对npm 的论点。 - 真正让我吃惊的是,它仍然在运行这个应用程序(如果我手动执行
npm install )…
无法使其正常工作,并且怀疑文件本身有什么奇怪的地方,我直接在Mac上创建了一个新的文件,这次使用的是vim。我输入了完全相同的指令,而且…现在它可以毫无问题地工作了。两个文件上的差异正好显示零差异。
有什么区别?什么能使第一个脚本不起作用?我怎么知道?
更新根据被接受的答案的建议,在错误的行尾返回后,我检查了多个东西。结果发现,由于我从Windows机器上复制了我的
。因此,除了运行dos2unix(必须使用Mac上的homebrew安装)之外,如果您使用的是git,请检查配置。
对。bash脚本对行尾敏感,无论是在脚本本身还是在它处理的数据中。它们应该有Unix风格的行尾,即每行以换行符(十进制10,十六进制0A,ASCII)结尾。好的。脚本中的DOS/Windows行尾
对于Windows或DOS样式的行尾,每行都以回车符结尾,后跟换行符。如果使用Windows行尾保存脚本文件,bash将文件视为好的。
1 2 3 4 5 6 7 | #!/bin/bash^M ^M cd"src"^M npm install^M ^M cd ..^M ./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs"src" &^M |
注:我用插入符号表示非打印字符,即
在这种情况下,回车(
- 由于没有名为
^M 的命令,所以它打印: command not found 。 - 由于没有名为
"src"^M 或src^M 的目录,所以打印: No such file or directory 。 - 它通过
install^M 而不是install 作为npm 的理由,导致npm 抱怨。
输入数据中的DOS/Windows行尾
如上所述,如果您有一个回车的输入文件:好的。
1 2 | hello^M world^M |
然后,在编辑器中以及在将其写入屏幕时,它看起来完全正常,但工具可能会产生奇怪的结果。例如,
1 2 | $ grep 'hello$' file.txt || grep -x"hello" file.txt (no match because the line actually ends in ^M) |
附加文本将覆盖行,因为回车将光标移动到行的开头:好的。
1 2 3 | $ sed -e 's/$/!/' file.txt !ello !orld |
字符串比较似乎会失败,即使在写入屏幕时字符串看起来是相同的:好的。
1 2 3 4 5 6 7 | $ a="hello"; read b < file.txt $ if [["$a" ="$b" ]] then echo"Variables are equal." else echo"Sorry, $a is not equal to $b" fi Sorry, hello is not equal to hello |
解决
解决方案是将文件转换为使用Unix样式的行尾。有很多方法可以做到这一点:好的。
这可以使用
1 | dos2unix filename |
在功能强大的文本编辑器(Sublime,Notepad++,而不是Notepad)中打开文件,并将其配置为使用Unix行尾保存文件,例如,使用VIM,在(重新)保存之前运行以下命令:好的。
1 | :set fileformat=unix |
如果您有支持
1 2 | sed -i 's/ $//' filename |
对于其他版本的
1 2 | sed 's/ $//' filename > filename.unix |
同样,可以使用
1 2 | tr -d ' ' <filename >filename.unix |
塞文温巴什
对于cygwin的bash端口,有一个自定义的
- Unix行尾:
Bourne-Again shell script, ASCII text executable 。 - MAC线端:
Bourne-Again shell script, ASCII text executable, with CR line terminators 。 - DOS行尾:
Bourne-Again shell script, ASCII text executable, with CRLF line terminators 。
维基百科有一篇优秀的文章,涵盖了标记一行文本结尾的多种不同方式、此类编码的历史以及新行如何在不同的操作系统、编程语言和互联网协议(如ftp)中处理。好的。带有经典Mac OS行结尾的文件
对于经典的mac os(pre-os x),每行以回车(十进制13,十六进制0d,ASCII)结束。如果用这样的行尾保存脚本文件,bash只能看到一行这样的长行:好的。
1 | #!/bin/bash^M^Mcd"src"^Mnpm install^M^Mcd ..^M./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs"src" &^M |
由于这一长行以octothorpe(
注:2001年,苹果推出了基于BSD衍生的NextStep操作系统的MacOSX。因此,OSX也使用了Unix风格的LF-only行结尾,从那时起,以CR结尾的文本文件变得非常罕见。不过,我认为值得展示bash如何尝试解释这些文件。好的。好啊。
另一种消除不需要的CR("
")字符的方法是运行
1 2 | $ tr -d ' ' < dosScript.py > nixScript.py |
在JetBrains产品(Pycharm、Phpsterm、Idea等)上,您需要在
和
来自一个副本,如果问题是文件的名称在末尾包含
1 2 3 4 5 | for f in *$' '; do mv"$f""${f%$' '}" done |
您首先要正确地修复导致这些文件名称损坏的原因(可能创建这些文件的脚本应该是
在Mac/Linux上最简单的方法是使用"touch"命令创建一个文件,用vi或vim编辑器打开这个文件,粘贴代码并保存。这将自动删除Windows字符。