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

时间:2010-04-17 19:51:20

标签: bash escaping

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

#!/bin/env bash
python -m SimpleHTTPServer

然而,这失败了:

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

-bash: !/bin/env: event not found

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

但我无法逃脱! \!在回显的字符串中产生\!\\!也是如此。

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

您是否有一些常规提示可以让我更容易理解逃避规则?

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

5 个答案:

答案 0 :(得分:12)

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

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

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

echo '#!/bin/env bash
python -m SimpleHTTPServer' >server.sh &&
chmod +x server.sh

或者printf

printf '#!/bin/env bash\npython -m SimpleHTTPServer\n' >server.sh &&
chmod +x server.sh

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

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


如果你需要包含单引号,你可以使用双引号作为最外面的引号,但是你必须担心双引号内发生了什么类型的插值(因shell而异,但通常是\$和``are all special and`在换行符之前有特殊含义;在{em> bash 中启用历史记录扩展时,!也很特殊:

echo "The answer is '\$foo'\!"

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

echo 'The answer is '\''foo'\''!'

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

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

cat <<\EOF
The answer is '$foo'!
EOF

EOF之前的反斜杠使这里的文档成为文字的,而不是引用的双引号,而不引用终止词。

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

frob -nitz="$(cat <<\EOF
The answer is '$foo'!
EOF
)"

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

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

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"

答案 1 :(得分:7)

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

echo '$hello world!'

如果您需要混淆,可以替代或停用引用:

echo '$5.00 on '"$horse"'!'
ls -ld ~/'$$'/*

解释转义也很容易:

echo $'Wake up!\7'
sort -n <<< $'4\n3\n8'

答案 2 :(得分:3)

使用-e,bashs buildin echo和/ bin / echo将\ n视为换行符:

echo -e '#!/bin/env bash\npython -m SimpleHTTPServer' 

但我只知道来自echo。

答案 3 :(得分:1)

避免“未找到事件”错误的最简单方法是关闭历史记录扩展:

set +o histexpand

尚未提及的一件事是echo的某些版本支持导致转义解释的-e选项:

$ echo -e 'two\nlines'
two
lines

答案 4 :(得分:1)

您也可以将换行存储在变量中:

NL='
'
printf "%q\n" "${NL}"
sort -n <<< "4${NL}3${NL}8"


# (yet another) example of shell string concatenation

# As a rule of thumb: 
# Put anything inside single quotes, 
# except "${variables}", "$(commands)" and "'single quotes'".

echo 'The answer is '\''foo'\''!'

echo 'The answer is '"'"'foo'"'"'!'  # concatenate 'str1'"str2"...

echo 'The answer is '\
     "'"\
     'foo'\
     "'"\
     '!'\
     "${NL}`date`"'!'


# ... and sometimes there simply is no need for ${NL} or \n

echo '#!/bin/env bash
python -m SimpleHTTPServer' > server.sh

echo '
line1
line2
line3
' > file