我正在Mac上制作NW.js应用程序,并想通过双击图标在开发模式下运行该应用程序。第一步,我试图使我的shell脚本正常工作。
在Windows上使用VSCode(我想节省时间),我在项目的根目录下创建了一个run-nw
文件,其中包含以下内容:
#!/bin/bash
cd "src"
npm install
cd ..
./tools/nwjs-sdk-v0.17.3-osx-x64/nwjs.app/Contents/MacOS/nwjs "src" &
但我得到以下输出:
$ 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)
[email protected] /usr/local/lib/node_modules/npm
: command not found
: No such file or directory
: command not found
我真的不明白:
\r\n
替换\n
(以防\r
产生问题),但是它什么也没有改变。 dirname
指令),或者它不知道cd
命令吗? install
的npm
参数npm install
)... 由于无法正常运行,并且怀疑文件本身有些奇怪,我这次使用vim在Mac上直接创建了一个新文件。我输入了完全相同的说明,然后...现在可以正常使用了。
两个文件的差异显示完全为零。
有什么区别?什么会使第一个脚本不起作用?我怎么知道?
更新资料
按照接受的答案的要求,在错误的行尾返回之后,我检查了很多事情。事实证明,由于我从Windows机器复制了
~/.gitconfig
,所以有了autocrlf=true
,因此每次在Windows下修改bash文件时,它将行尾重新设置为\r\n
。因此,除了运行dos2unix(您必须在Mac上使用Homebrew进行安装)之外,如果您使用的是Git,请检查配置。
最佳答案
是。 Bash脚本在脚本本身及其处理的数据中都对行尾敏感。它们应具有Unix样式的行尾,即每行以换行字符(十进制,ASCII十六进制0A)结尾。
脚本中的DOS / Windows行尾
使用Windows或DOS样式的行尾,每行都以回车符和换行符结尾。您可以在cat -v yourfile
的输出中看到这个不可见的字符:
$ cat -v yourfile
#!/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
或以C转义表示形式的\r
)视为空白。 Bash将shebang之后的第一行(由一个回车符组成)解释为要运行的命令/程序的名称。^M
的命令,它会打印: command not found
"src"^M
(或src^M
)的目录,所以它会打印: No such file or directory
install^M
而不是install
传递为npm
的参数,这会导致npm
抱怨。 输入数据中的DOS / Windows行尾
像上面一样,如果您有一个包含回车符的输入文件:
hello^M
world^M
那么它将在编辑器中以及将其写入屏幕时看起来完全正常,但是工具可能会产生奇怪的结果。例如,grep
将无法找到明显存在的行:$ grep 'hello$' file.txt || grep -x "hello" file.txt
(no match because the line actually ends in ^M)
附加的文本将改写该行,因为回车将光标移动到该行的开头:$ sed -e 's/$/!/' file.txt
!ello
!orld
字符串比较似乎失败,即使在写入屏幕时字符串看起来相同:$ 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样式的行尾。有多种方法可以实现:
dos2unix
程序完成:dos2unix filename
:set fileformat=unix
sed
或-i
选项的--in-place
实用程序版本,例如GNU sed
,则可以运行以下命令来剥离尾随回车符:sed -i 's/\r$//' filename
对于其他版本的sed
,您可以使用输出重定向来写入新文件。确保为重定向目标使用其他文件名(以后可以重命名)。sed 's/\r$//' filename > filename.unix
tr
转换过滤器从其输入中删除不需要的字符:tr -d '\r' <filename >filename.unix
西格温·巴什(Cygwin Bash)
通过Cygwin的Bash端口,可以自定义
igncr
选项,该选项可以设置为忽略行尾的回车符(可能是因为其许多用户使用本机Windows程序来编辑其文本文件)。可以通过运行
set -o igncr
为当前 shell 启用此功能。设置此选项仅适用于当前的shell进程,因此在寻找带有多余回车符的文件时很有用。如果您经常遇到带有DOS行尾的shell脚本,并且希望永久设置此选项,则可以将一个名为
SHELLOPTS
(所有大写字母)的环境变量设置为包括igncr
。 Bash使用此环境变量在启动时设置 shell 程序选项(在读取任何启动文件之前)。有用的工具
file
实用程序对于快速查看文本文件中使用的行尾很有用。这是每种文件类型的打印内容:Bourne-Again shell script, ASCII text executable
Bourne-Again shell script, ASCII text executable, with CR line terminators
Bourne-Again shell script, ASCII text executable, with CRLF line terminators
cat
实用程序的GNU版本具有-v, --show-nonprinting
选项,该选项显示非打印字符。dos2unix
实用程序专门用于在Unix,Mac和DOS行尾之间转换文本文件。有用的链接
Wikipedia的excellent article涵盖了标记文本行结尾的许多不同方式,这种编码的历史记录以及在不同的操作系统,编程语言和Internet协议(protocol)(例如FTP)中如何处理换行符。
具有经典Mac OS行尾的文件
使用Classic Mac OS(在OS X之前的版本),每行以回车符(ASCII中的十进制13,十六进制0D)终止。如果脚本文件以这样的行结尾保存,那么Bash只会看到一条长行,如下所示:
#!/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(#
)开头,因此Bash将该行(以及整个文件)视为一条注释。注意:2001年,Apple推出了Mac OS X,它基于BSD衍生的NeXTSTEP操作系统。结果,OS X还使用Unix风格的仅LF的行尾,从那以后,以CR终止的文本文件变得极为罕见。不过,我认为值得展示Bash如何尝试解释此类文件。
关于bash - Shell脚本对编码和行尾敏感吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/64269709/