我正在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

我真的不明白:
  • 似乎需要空行作为命令。在我的编辑器(VSCode)中,我尝试用\r\n替换\n(以防\r产生问题),但是它什么也没有改变。
  • 似乎找不到文件夹(带或不带dirname指令),或者它不知道cd命令吗?
  • 似乎不理解installnpm参数
  • 真正令我感到困惑的部分是它仍然可以运行该应用程序(如果我手动执行了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
    
  • 在功能强大的文本编辑器(Sublime,Notepad ++,不是Notepad)中打开文件并将其配置为保存带有Unix行尾的文件,例如使用Vim,在保存(重新)之前运行以下命令:
    :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实用程序对于快速查看文本文件中使用的行尾很有用。这是每种文件类型的打印内容:
  • 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
  • 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/

    10-16 17:16