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

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"

bash: ... event not found。

这是怎么回事?逃脱感叹号的正确语法是什么?

评论

解决于bash 4.4+

#1 楼

感叹号是bash历史扩展中的一部分。要使用它,您需要将其括在单引号中(例如:'http://example.org/!132'),或者直接在字符前面使用反斜杠(\)将其转义(例如:"http://example.org/\!132")。在感叹号阻止历史扩展之前使用反斜杠,但是在这种情况下不会删除反斜杠。因此,最好使用单引号,这样就不会在URL上将文字反斜杠传递给curl

评论


实际上,“ http://example.org/\!132”在扩展时没有解释反斜杠(我相信是POSIX合规性原因)。

–克里斯唐(Chris Down)
2012年3月3日19:14

@ChrisDown,我试图澄清这是文本中的第二个选项。感谢您指出混淆的可能。

–丹尼尔·皮特曼(Daniel Pittman)
2012年3月3日在22:30

记录:尝试转义“!”不是可移植的。最佳做法建议是始终用单引号(!)引号!相关信息:“ ^”(脱字符号),是非字符,需要引用才能实现可移植性。最后,“!”不应在if语句中使用;尽可能使用它作为参数进行测试(同样由于Solaris / bin / sh)。

–尼古拉斯·威尔逊(Nicholas Wilson)
2012年10月18日上午10:31

只有单引号对我有用。 zsh仍在解释\!和双引号。

–orkoden
2014年5月21日在16:29

在Solaris(旧的XPG4以前的旧外壳)上,“ ^”是|的别名。并用于创建管道。如果您要向客户发送脚本,但不确定他们将在哪个shell中运行脚本,则必须对它们全部进行测试!

–尼古拉斯·威尔逊(Nicholas Wilson)
18年1月16日在9:47

#2 楼

除了Daniel给出的答案外,如果您不使用set +H,也可以完全关闭历史记录扩展。

评论


整天关闭历史记录扩展是我整天听到的最好建议!当有更好的替代方法(使用Ctrl-R进行增量历史搜索)时,历史扩展会很危险,而且会造成拜占庭式的麻烦。这些替代方法可以预览和编辑命令,因此您不会盲目地使用命令!-14,尽管您原来在!-12糟糕,恰好是rm -rf *。注意安全。禁用历史记录扩展!避免!

– aculich
2012年6月6日在1:09



最大的答案:历史扩展是巨大的安全风险!它可以用来通过特制的URL攻击Unix。

– dan
2015年6月15日7:30



@aculich,或仅使用POSIX指定的命令fc -14。但是,确实可以在不启用历史记录扩展的情况下执行此操作。我个人使用!$和!vi和sudo !!甚至git add!vi:$经常足以保证启用历史记录扩展。

–通配符
18年1月16日在5:02

我想将其添加到我的Shell RC文件中。我只把它当作整洁的“把戏”

– TonyH
19年8月22日在14:02

另外,IMO还提供更多描述性语法来关闭bash历史记录扩展:set + o histexpand;另请参见superuser.com/a/133782

–ssc
20年6月28日在10:15

#3 楼

我个人会用单引号引起来,但出于完整性考虑,由于它是一个URL,因此您可以将!编码为%21,例如curl -v http://example.org/%21132

#4 楼

这也可以做到,因为bash将相邻的字符串连接在一起,所以可以工作。

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

当您还有其他需要外壳扩展的内容时,此方法特别有用,因此您不能在整个字符串中使用单引号:

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

命令行提示符中的历史记录扩展。
所以这可能是提示符中的问题,但不是Shell脚本文件中的问题。

评论


有很多方法可以使Unix命令和英语句子使用比他们需要的字符更多的字符,并使它们比需要的字符更加混乱。这比第一个/接受的/投票最高的答案(即将整个URL放在单引号中)有什么优势?

– G-Man说“恢复莫妮卡”
15年6月15日在7:10

@ G-Man:它告诉了另一种构造bash参数的方法。我不知道这种方法。学习新东西没错。

– Sahil Singh
16年7月6日在12:30



@SahilSingh这是新的吗?它连接三个字符串,两个字符串用双引号引起来,一个字符串用单引号引起来。这里没有嵌套。

–拉斐尔
17 Mar 10 '17 at 6:44

@ G-Man并不明显,当您将2个字符串彼此相邻放置时,它们会串联在一起。 printf(“ hello”“ world”)也可以在c中工作,但是printf(“ hello”'w')不能工作,所以您知道知道bash可以容纳这样的表达式对我来说是新的,但是我同意效用来看,这并不优越。我喜欢这个答案,马克·舒斯特也一样。

– Sahil Singh
17 Mar 10 '17 at 7:06

@ G-Man当确实要在同一字符串中进行其他字符串扩展时,它也很有用。这是分离两种类型的报价行为的简便方法。

– WAF
17年5月1日在15:09

#5 楼

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

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"


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

#6 楼

从Bash 4.3开始,您现在可以使用双引号来引用历史记录扩展字符:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!


评论


这在echo之外不起作用,echo似乎单独处理此问题

– phil 294
17年9月30日在12:04

@Blauhirn这与echo无关,与引用和正在运行的bash版本无关。

– Flimm
17-10-1在13:38

这个答案是错误的,应该删除。您的bash版本与爆炸没有得到扩展没有关系,这是由于在您的示例中!后面跟着“行尾”,这可以防止外壳尝试扩展外壳。尝试echo“!Hello World”,您将看到bash会以bash回复:!Hello:事件未找到。有关更多详细信息,请参见手册

–don_crissti
17/12/19在16:47



#7 楼

对于在Windows中使用git bash的用户,@ DanielPittman可接受的答案有效。但是,应将反斜杠(\)替换为正斜杠(/)。

例如,在unix中,它看起来像这样:重点放在授权标头部分中的正斜杠上)

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS\!ZR412543s'

评论


这没有多大意义。您将论点单引号括起来,因此无论是否包含斜杠,感叹号都不会导致历史扩展。

–通配符
18年1月16日在5:03

哦,对了。我之所以仅发布此答案,是因为当我使用Daniel的答案(使用反斜杠)时,会弹出一个错误。

– SamuelDev
18年1月16日在5:12