如何回响一声巨响!


59

我试图echo通过将内容放入文件而不是使用编辑器打开脚本来创建脚本

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

输出

bash:!/ bin / bash:找不到事件

我已经分离出这种奇怪的行为的爆炸

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • 为什么爆炸会引起这种情况?
  • bash指的这些“事件”是什么?
  • 如何解决此问题,然后在屏幕或文件中打印“#!/ bin / bash”?

Answers:


59

尝试使用单引号。

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

发生问题是因为bash正在其历史记录中搜索!/ bin / bash。使用单引号可以避免这种行为。


3
它还会禁用字符串插值:(
Hubro,2015年

这才是重点!您可以使用双引号将其他参数添加到同一echo命令,并在这些部分上进行插值。
Richm

3
您也可以在bash中使用C样式字符串$''。例如,echo $'1!\n2!\n3!\n'打印每个数字,后跟一个爆炸和换行符。
spelufo

1
输入多行字符串时,在单引号中加上bang并没有帮助:
bash-

1
@kbolino你是对的。因此,完整的示例字符串为:echo '0123'"$var"'456',请注意两对单引号和一对双引号。
phyatt

16

正如Richm所说,bash正在尝试做历史比赛。避免这种情况的另一种方法是使用来逃避爆炸\

$ echo \#\!/bin/bash
#!/bin/bash

尽管要注意在双引号内,\不会将其删除:

$ echo "\!"
\!

8
在bash中,"\!"实际上扩展为\!,而不是!,据说是为了符合POSIX。
Mikel

@Mikel叹息。zsh和bash之间的差异如此之多
Michael Mrozek

即使输入多行字符串也可以使用。记住1.输入爆炸声时不要在字符串外,并且2.在大多数情况下使用此方法(反斜杠)似乎效果最小。
宾基

1
请注意,bash在执行脚本时不会执行历史搜索,因此无需在其中执行任何特殊操作。
宾基

没有任何语法可以简单地!在双引号内转义而没有任何魔术行为吗?这
太疯狂了

13

!开始历史记录替换(“事件”是命令历史记录中的一行);例如,!ls扩展到包含的最后一个命令行ls,并!?foo扩展到包含的最后一个命令行foo。您还可以提取特定的单词(例如,!!:1引用上一个命令的第一个单词)以及更多内容。有关详细信息,请参见手册。

发明此功能的目的是在命令行版本很原始的时候快速调用以前的命令。使用现代的shell(至少是bash和zsh)以及复制和粘贴功能,历史扩展已不再像以前那样有用了,它仍然很有用,但是如果没有它,您可以轻松解决。

您可以通过设置histchars变量来更改触发历史替换的字符。如果您很少使用历史记录替换,则可以将eg设置为histchars='¡^'¡而不是触发历史记录扩展!。您甚至可以完全关闭该功能set +o histexpand


3

为了能够在特定的命令行上禁用历史记录扩展,您可以将其space用作第三个字符$histchars

histchars='!^ '

然后,如果您输入的命令前面有空格,则不会执行历史记录扩展。

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

但是请注意,当$HISTCONTROL包含时ignorespace,也将使用前导空格来告诉您bash不要在历史记录中记录命令行。

如果要同时使用两个功能,则需要选择另一个字符作为的第三个字符$histchars。您需要一个不影响命令解释方式的命令。一些选择:

  • 使用反斜杠(\):\echo foo有效,但是具有禁用别名或关键字的副作用。
  • TAB:将其插入第一个位置,您需要按一下Ctrl+VTab
  • 如果你不介意输入两个键,你可以挑选,通常不会出现在第一位置(任何字符%@?,选择你自己的),并创建一个空的别名为它:

    histchars='!^%'
    alias %=

    然后输入该字符,空格和您的命令:

    bash-4.3$ % echo !!
    !!

(您将无法在禁用了历史记录替换的情况下记录命令。还要注意,默认的第3个字符$histchars是,#因此注释中的历史记录扩展不会完成。如果您进行了更改,请在提示时,您应该意识到以下事实:!序列可能会在此处扩展。


2

建议的解决方案不适用于以下示例:

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

在这种情况下,可以使用其八进制ASCII代码来打印bang:

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
在您的第一个命令中,!在双引号之间,如其他答案所示,它保留了其历史扩展的含义。您可以使用 bash -c 'echo '\''hello World!'\'bash -c "echo 'hello World"\!"'"
吉尔斯(Gilles)2010年

如果没有-i参数,则环境可能会更改(例如〜/ .profile无法运行)。但是,执行-i意味着您的.profile中的任何输出都将被捕获到您实际要运行的命令的输出中。
布伦特

-1

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

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

4
不,这只是!其次"就是不再特殊。echo "foo!"就OK了,echo "foo!bar"是不是
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.