我对命令行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命令行中转义?包括如何在我的示例中正确输出换行符和感叹号。
答案 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