不能在 bash 中使用感叹号 (!)?

net*_*ain 148 bash special-characters quoting

我正在尝试使用 curl 命令访问!路径中带有感叹号 ( )的 http url 。例如:

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"
Run Code Online (Sandbox Code Playgroud)

控制台回复bash: ... event not found.

这里发生了什么?逃避感叹号的正确语法是什么?

Dan*_*man 156

感叹号是 bash 历史扩展的一部分。要使用它,您需要将其括在单引号中(例如: 'http://example.org/!132')或\在字符前使用反斜杠 ( )直接转义它(例如:"http://example.org/\!132")。

请注意,在双引号中,感叹号前的反斜杠会阻止历史扩展,但在这种情况下不会删除反斜杠。因此最好使用单引号,这样您就不会将文字反斜杠curl作为 URL 的一部分传递给。

  • `"http://example.org/\!132"` 实际上扩展而不解释反斜杠(POSIX 合规性原因,我相信)。 (11认同)
  • 只有单引号对我有用。zsh 仍在解释 `\!` 和双引号。 (8认同)
  • 为了记录:尝试转义“!”是不可移植的。最佳实践建议是始终引用(单引号)“!”。相关:“^”(插入符号)是一个非元字符,需要引用以方便移植。最后, ”!” 不应在 if 语句中使用;如果可能,使用它作为参数进行测试(再次因为 Solaris /bin/sh)。 (7认同)
  • 在 Solaris(XPG4 之前的垃圾外壳)上,'^' 是 `|` 的别名,用于创建管道。如果您要向客户发送脚本并且不能确定他们将在什么 shell 中运行它,那么您必须对他们全部进行测试! (2认同)

Chr*_*own 75

除了丹尼尔给出的答案外,如果您不将历史扩展与set +H.

  • 完全关闭历史扩展是我一整天听到的最好的建议!历史扩展是危险的 [拜占庭](http://www.thegeekstuff.com/2011/08/bash-history-expansion/) 当有 [更好的选择](http://www.gnu.org/software /bash/manual/html_node/Commands-For-History.html)(使用 `Ctrl-R` 进行增量历史搜索),让你预览和编辑你的命令,这样你就不会盲目地使用命令 `!-14`你虽然在 `!-12` 那个,哎呀,碰巧是 `rm -rf *`。注意安全。禁用历史扩展!避开`!`! (19认同)
  • 最大的答案:历史扩展是一个巨大的安全风险!它可用于通过精心制作的 URL 攻击您的 Unix。 (6认同)

Aar*_*sco 24

我个人会做单引号,但为了完整起见,我还要注意,因为它是一个 URL,您可以将其编码!%21,例如curl -v http://example.org/%21132.


mug*_*896 20

这也可以

curl -v "http://example.org/"'!'"287s87asdjh2/somepath/someresource"
或者
curl -v "http://example.org/"\!"287s87asdjh2/somepath/someresource"

之所以有效,是因为 bash 连接相邻的字符串。当您有其他需要 shell 扩展的东西时,这种方法特别有用,因此您不能对整个字符串使用单引号:

curl -v 'http://example.org/!'"287s87asdjh2/${basepath}/someresource"

!字符用于命令行提示符中的历史扩展。
所以这可能是提示中的问题,但不是 shell 脚本文件中的问题。
正如您所看到的,即使在双引号中,历史扩展也能正常工作。

  • @G-Man :它讲述了另一种构造 bash 参数的方法。我不知道这种方法。学习新东西没有错。 (3认同)
  • @G-Man 当有其他字符串扩展 _does_ 希望在同一字符串中发生时,这也很有用。这是区分两种引用行为的简单方法。 (3认同)

Pre*_*rem 12

我遇到了同样的问题,我的简单解决方案是使用一个变量:

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"
Run Code Online (Sandbox Code Playgroud)

这里的简单性在于 (1) 它可以跨 shell 和命令移植 (2) 不需要知道转义语法和 ASCII 代码。


Fli*_*imm 5

从 Bash 4.3 开始,您现在可以使用双引号来引用历史扩展字符(编辑:只要感叹号位于字符串末尾或后跟空格)

$ $SHELL --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
$ echo "Hello! World"
Hello! World
$ echo "Hello!World"
bash: !World: event not found
$ echo 'Hello!World'
Hello!World
Run Code Online (Sandbox Code Playgroud)