我正在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
我真的不明白:
\r\n
替换为\n
(如果\r
出现问题),但它什么都没有改变。dirname
指令),或者它可能不知道cd
命令?install
npm
参数
npm install
)...... 无法使其正常工作,并怀疑文件本身有些奇怪,我这次使用vim直接在Mac上创建了一个新的。我输入完全相同的说明,并且......现在它没有任何问题 两个文件上的差异显示零差异。
有什么区别?什么可以使第一个脚本不起作用?我该如何找到?
根据接受的答案的建议,在错误的行结束回来后,我检查了多个事情。事实证明,因为我从我的Windows机器复制了~/.gitconfig
,所以我有autocrlf=true
,因此每次我在Windows下修改bash文件时,都会将行结尾重新设置为\r\n
。
因此,除了运行dos2unix(您必须使用Mac上的Homebrew安装),如果您正在使用Git,请检查您的配置。
答案 0 :(得分:47)
是。 Bash脚本 对行结尾敏感,无论是在脚本本身还是在它处理的数据中。它们应该具有Unix风格的行结尾,即每行以换行符结尾(十进制10,ASCII中的十六进制0A)。
对于Windows或DOS样式的行结尾,每行都以回车符后跟换行字符终止。如果使用Windows行结尾保存脚本文件,Bash会将该文件视为
#!/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
);这与cat -v
和Vim使用的技术相同。
在这种情况下,回车符(^M
或\r
)不会被视为空格。 Bash将shebang(由单个回车符组成)后面的第一行解释为要运行的命令/程序的名称。
^M
的命令,因此会打印: command not found
"src"^M
(或src^M
)的目录,因此会打印: No such file or directory
install^M
而不是install
作为npm
的参数传递,导致npm
投诉。如上所述,如果您有一个带回车符的输入文件:
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 ++,而不是记事本)中打开文件,并将其配置为使用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程序来编辑他们的文本文件)。设置此选项适用于当前 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行结尾之间转换文本文件。
维基百科有excellent article涵盖了标记文本行结尾的许多不同方式,此类编码的历史以及如何在不同的操作系统,编程语言和Internet协议(例如,FTP)中处理换行符)。
使用Classic Mac OS(OS X之前),每行都以回车符(十进制13,ASCII中的十六进制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推出了基于BSD派生的NeXTSTEP操作系统的Mac OS X.因此,OS X也使用Unix风格的LF专线结束,从那时起,以CR终止的文本文件变得非常罕见。尽管如此,我认为值得展示Bash如何尝试解释这些文件。
答案 1 :(得分:3)
如果您使用 read
命令读取(或可能)DOS/Windows 格式的文件(或管道),您可以利用read
会从行的开头和结尾修剪空白。如果你告诉它回车是空格(通过将它们添加到 IFS
变量),它会从行尾修剪它们。
在 bash(或 zsh 或 ksh)中,这意味着您将替换此标准习语:
IFS= read -r somevar # This will not trim CR
这样:
IFS=$'\r' read -r somevar # This *will* trim CR
(注意:-r
选项与此无关,这通常是避免修改反斜杠的好主意。)
如果您不使用 IFS=
前缀(例如,因为您想将数据拆分为字段),那么您可以替换:
read -r field1 field2 ... # This will not trim CR
这样:
IFS=$' \t\n\r' read -r field1 field2 ... # This *will* trim CR
如果您使用的 shell 不支持 $'...'
引用模式(例如破折号,某些 Linux 发行版上的默认 /bin/sh),或者您的脚本甚至可能 用这样的 shell 运行,那么你需要稍微复杂一点:
cr="$(printf '\r')"
IFS="$cr" read -r somevar # Read trimming *only* CR
IFS="$IFS$cr" read -r field1 field2 ... # Read trimming CR and whitespace, and splitting fields
注意,正常情况下,当你改变IFS
时,你应该尽快把它恢复正常,以免出现奇怪的副作用;但在所有这些情况下,它都是 read
命令的前缀,因此它只影响那个命令,之后不必重新设置。
答案 2 :(得分:2)
摆脱不需要的CR('\ r')字符的另一种方法是运行tr
命令,例如:
$ tr -d '\r' < dosScript.py > nixScript.py
答案 3 :(得分:1)
答案 4 :(得分:0)
在MAC / Linux上最简单的方法-使用“ touch”命令创建文件,使用VI或VIM编辑器打开该文件,粘贴代码并保存。这将自动删除Windows字符。
答案 5 :(得分:0)
来自一个重复项,如果问题是您的名称的文件末尾包含^M
,则可以使用
for f in *$'\r'; do
mv "$f" "${f%$'\r'}"
done
您应该正确地解决导致这些文件最初名称损坏的原因(可能创建脚本的脚本应该dos2unix
然后重新运行?),但是有时这是不可行的。
答案 6 :(得分:0)
我试图从Windows启动我的docker容器并得到了这个信息:
Bash script and /bin/bash^M: bad interpreter: No such file or directory
我使用的是git bash,问题出在git的配置上,然后我按照下面的步骤操作就可以了。它将配置Git在结帐时不转换行尾:
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
答案 7 :(得分:0)
我在WSL中使用git时遇到了这个问题。
git具有一项功能,可以根据您使用的操作系统更改文件的行尾,在Windows上,请确保行尾为/r/n
,这与仅使用/n
的Linux不兼容。
您可以通过在git根目录中添加文件名.gitattributes
并添加以下行来解决此问题:
config/* text eol=lf
run.sh text eol=lf
在此示例中,config
目录内的所有文件将仅具有换行符结尾,并且run.sh
文件也将具有换行符。
答案 8 :(得分:0)
答案 9 :(得分:0)
出于完整性考虑,我将指出another solution,它可以永久解决此问题,而无需一直运行dos2unix:
sudo ln -s /bin/bash `printf 'bash\r'`
答案 10 :(得分:0)