shell脚本对编码和行尾敏感吗?

yk9xbfzb  于 2021-05-27  发布在  Hadoop
关注(0)|答案(9)|浏览(449)

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

npm@3.10.3 /usr/local/lib/node_modules/npm  
: command not found  
: No such file or directory  
: command not found

我真的不明白:
它似乎以空行作为命令。在我的编辑器(vscode)中,我尝试替换 \r\n\n (如果 \r 制造问题),但它什么也没改变。
似乎找不到文件夹(有或没有 dirname 或者它不知道 cd 命令?
似乎它不明白 install 论据 npm 真正让我感到奇怪的是,它仍然在运行这个应用程序(如果我做了一个测试的话) npm install 手动)。。。
由于无法使其正常工作,并且怀疑文件本身有什么奇怪的地方,我直接在mac上创建了一个新的文件,这次使用的是vim。我输入了完全相同的指令,然后。。。现在它的工作没有任何问题。
两个文件的差异显示完全没有差异。
有什么区别?什么能使第一个脚本不起作用?我怎么知道?

更新

按照接受答案的建议,在错误的行尾返回后,我检查了多个内容。原来自从我抄了我的 ~/.gitconfig 在我的windows机器上,我 autocrlf=true ,所以每次我在windows下修改bash文件时,它都会将行尾重新设置为 \r\n .
因此,除了运行dos2unix(必须在mac上使用自制软件安装),如果您使用的是git,请检查您的配置。

okxuctiv

okxuctiv1#

我在wsl中使用git时遇到了这个问题。git有一个特性,它可以根据您使用的操作系统更改文件的行尾,在windows上,它可以确保行尾是正确的 /r/n 与只使用 /n .
您可以通过添加文件名来解决此问题 .gitattributes 添加到git根目录,并添加以下行:

config/* text eol=lf
run.sh text eol=lf

在本例中,所有文件 config 目录将只有换行符和换行符 run.sh 还有文件。

0lvr5msh

0lvr5msh2#

对。bash脚本对行结束非常敏感,无论是在脚本本身还是在它处理的数据中。它们应该有unix风格的行尾,即每行以换行符(十进制10,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 插入符号或 \r 在c语言中,转义符号)不被视为空白。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端口,有一个自定义 igncr 选项,可以设置为忽略行尾中的回车(可能是因为许多用户使用本机windows程序编辑文本文件)。可以通过运行 set -o igncr .
设置此选项仅适用于当前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 gnu版本的 cat 实用程序具有 -v, --show-nonprinting 显示非打印字符的选项。
这个 dos2unix 这个实用程序是专门为在unix、mac和dos行尾之间转换文本文件而编写的。

有用的链接

wikipedia有一篇很好的文章,介绍了标记一行文本结尾的许多不同方法,这种编码的历史,以及在不同的操作系统、编程语言和internet协议(如ftp)中如何处理换行。

具有经典mac os行尾的文件

对于经典的mac os(os x之前的版本),每行都以回车结束(十进制13,十六进制0d,ascii)。如果脚本文件以这样的行结尾保存,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

因为这条长长的线是从一只章鱼开始的( # ),bash将行(和整个文件)视为单个注解。
注:2001年,苹果推出了基于bsd衍生的nextstep操作系统的MacOSX。因此,osx也使用unix风格的lf-only行尾,从那时起,以cr结尾的文本文件变得极为罕见。不过,我认为有必要展示bash如何尝试解释这些文件。

j5fpnvbx

j5fpnvbx3#

在mac/linux上最简单的方法是使用“touch”命令创建一个文件,用vi或vim编辑器打开这个文件,粘贴代码并保存。这将自动删除windows字符。

fkaflof6

fkaflof64#

在jetbrains产品(pycharm、phpstorm、idea等)上,您需要单击 CRLF / LF 在两种行分隔符之间切换的步骤( \r\n 以及 \n ).

kuuvgm7e

kuuvgm7e5#

如果您使用的是bbedit这样的文本编辑器,您可以在状态栏上进行编辑。有一个可供切换的选项。

inb24sb2

inb24sb26#

摆脱不需要的cr('\r')字符的另一种方法是运行 tr 命令,例如:

$ tr -d '\r' < dosScript.py > nixScript.py
8iwquhpp

8iwquhpp7#

为了完整起见,我将指出另一种解决方案,它可以永久地解决这个问题,而无需一直运行dos2unix:

sudo ln -s /bin/bash `printf 'bash\r'`
3vpjnl9f

3vpjnl9f8#

如果问题是文件名中包含 ^M 最后,您可以使用

for f in *$'\r'; do
    mv "$f" "${f%$'\r'}"
done

您应该首先修复导致这些文件名称损坏的原因(可能是创建这些文件的脚本) dos2unix 但有时这是不可行的。
这个 $'\r' 语法是特定于bash的;如果你有一个不同的shell,也许你需要使用其他的符号。也许还能看到sh和bash之间的区别

wooyq4lh

wooyq4lh9#

我正试图从windows启动docker容器,结果是:

Bash script and /bin/bash^M: bad interpreter: No such file or directory

我使用的是gitbash,问题出在git配置上,然后我只做了下面的步骤,就成功了。它将git配置为在 checkout 时不转换行尾: git config --global core.autocrlf input 删除本地存储库
再克隆一次。
非常感谢jason harmon的链接:https://forums.docker.com/t/error-while-running-docker-code-in-powershell/34059/6
在那之前,我试过这个,但没用: dos2unix scriptname.sh sed -i -e 's/\r$//' scriptname.sh sed -i -e 's/^M$//' scriptname.sh

相关问题