理解如何在命令行中转义字符所需的bash提示

Jes*_*sen 1 bash escaping

我对命令行bash的了解在特定领域缺失:我经常忘记如何正确地逃避角色.今天我想将这个字符串回显到一个文件中:

#!/bin/env bash
python -m SimpleHTTPServer
Run Code Online (Sandbox Code Playgroud)

但是,这失败了:

echo "#!/bin/env bash\npython -m SimpleHTTPServer" > server.sh && chmod +x server.sh

-bash: !/bin/env: event not found
Run Code Online (Sandbox Code Playgroud)

这是正确的:记住逃避!或bash会认为这是一个特殊的bash事件命令.

但我无法逃脱!\!收益率\!在呼应字符串,也是如此\\!.

此外,\n不会转换为换行符.

你有一些一般性的提示,让我更容易理解逃避规则吗?

要非常精确,我会接受一个答案,告诉我哪些字符应该在bash命令行中转义?包括如何在我的示例中正确输出换行符和感叹号.

Chr*_*sen 12

首先,shell引用不像C字符串.仅仅因为你有一个\n引号之间没有在实际字符串中添加换行符.它有时会有点混乱,因为某些版本echo会解释反斜杠组合并输出换行符.此行为取决于您使用的确切shell,传递给哪些选项echo以及设置了哪些shell选项,因此对于跨平台使用它并不十分可靠.printf更标准化(它是一个shell内置程序或大多数系统上的外部程序),但它不存在于(非常?)旧系统上.

正如Ignacio Vazquez-Abrams所说,单引号是一个答案.$''他在最后提到的变体虽然不能在所有shell中移植(它不是shell语言的POSIX标准,而更简单的shell不支持它(尽管ksh,bashzsh都支持)).

您甚至可以在单个带引号的字符串中包含文字换行符:

echo '#!/bin/env bash
python -m SimpleHTTPServer' >server.sh &&
chmod +x server.sh
Run Code Online (Sandbox Code Playgroud)

或者,用printf:

printf '#!/bin/env bash\npython -m SimpleHTTPServer\n' >server.sh &&
chmod +x server.sh
Run Code Online (Sandbox Code Playgroud)

您应该始终注意第一个参数,printf但是如果有任何使用过的%字符,它们将被解释为格式说明符(即您通常不应该使用未知字符串作为第一个参数printf).

如果不需要插值,请使用单引号.它们允许您包含除单引号本身之外的任何文字.如果你需要一些类似C的转义序列,可以将它们放在第一个参数中printf或使用$''(如果你的shell支持它).


如果您需要包括单引号,可以使用双引号作为最外层的引号,但你不必担心双引号内会发生什么样的插值(由外壳不同,但通常\,$和`` are all special and`有一个特殊的意思是当它紧接在换行符之前; !bash中启用历史记录扩展时也是特殊的:

echo "The answer is '\$foo'\!"
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用带有转义单引号的多个单引号字符串连接:

echo 'The answer is '\''foo'\''!'
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,如果事情变得过于复杂,那么思考或阅读并不是太漂亮.

当面对想要在文字字符串中包含单引号时,您可能会改为使用带引号的 "here here":

cat <<\EOF
The answer is '$foo'!
EOF
Run Code Online (Sandbox Code Playgroud)

前面的反斜杠EOF使这里的文档成为文字的,而不是双引号 - 你得到的没有引用终止词.

这里的文档很容易输出(cat)或作为输入发送到其他命令,但它们不像参数那样方便使用:

frob -nitz="$(cat <<\EOF
The answer is '$foo'!
EOF
)"
Run Code Online (Sandbox Code Playgroud)

但是,如果您需要大量包含单引号和双引号内特殊字符的文字数据,这样的构造对于可读性(和可写性)可能是一个巨大的好处.注意:与所有其他用途一样$(),这将从here文档的内容中获取尾随换行符.

如果你需要尾随换行符,你可以得到它们,但这是额外的工作:

s="$(cat <<\EOF


This will have leading and trailing newlines.

It takes as literal everything except a line with only "EOF" on it!$@#&\
(and the "EOF" bit can be changed by using a different word after <<)

The trailing 'xxx' will be trimmed off by the next command.
It is here to protect the otherwise trailing newlines that $() would strip off.


xxx
EOF
)"
s="${s%xxx}"
frob -nitz="$s"
Run Code Online (Sandbox Code Playgroud)


Ign*_*ams 7

单引号禁止所有转义和替换:

echo '$hello world!'
Run Code Online (Sandbox Code Playgroud)

如果您需要混淆,可以替换或禁用引用:

echo '$5.00 on '"$horse"'!'
ls -ld ~/'$$'/*
Run Code Online (Sandbox Code Playgroud)

解释逃逸也很容易:

echo $'Wake up!\7'
sort -n <<< $'4\n3\n8'
Run Code Online (Sandbox Code Playgroud)