如何呼应一声巨响!

Ste*_*fan 62 command-line bash echo

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

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command
Run Code Online (Sandbox Code Playgroud)

输出

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

我已将这种奇怪的行为与bang隔离开来。

$ echo !
!  

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

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
Run Code Online (Sandbox Code Playgroud)
  • 为什么 bang 会导致这个?
  • bash 所指的这些“事件”是什么?
  • 我如何解决这个问题并将“#!/bin/bash”打印到屏幕或我的文件?

Ric*_*chm 66

尝试使用单引号。

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

echo '#!'

echo '#!/bin/bash'
Run Code Online (Sandbox Code Playgroud)

出现此问题是因为 bash 正在搜索 !/bin/bash 的历史记录。使用单引号可以避免这种行为。

  • 它还禁用字符串插值:( (3认同)
  • 你也可以在 bash 中使用 C 风格的字符串和 `$''`。例如,`echo $'1!\n2!\n3!\n'` 打印每个数字后跟一个 bang 和一个换行符。 (3认同)
  • 输入多行字符串时,将 bang 放在单引号中对我没有帮助: ![bash—单引号不会转义 bang (for real)](http://i.imgur.com/5HVVTaE.png) (2认同)

Mic*_*zek 20

正如Richm 所说,bash 正在尝试进行历史匹配。另一种避免它的方法是用一个 来逃避爆炸\

$ echo \#\!/bin/bash
#!/bin/bash
Run Code Online (Sandbox Code Playgroud)

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

$ echo "\!"
\!
Run Code Online (Sandbox Code Playgroud)

  • 在 bash 中,```"\!"``` 实际上扩展为 ```\!```,而不是 ```!```,据说是为了符合 POSIX。 (10认同)

Gil*_*il' 15

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

发明此功能是为了在命令行版本还很原始的时候快速调用以前的命令。使用现代 shell(至少是 bash 和 zsh)和复制粘贴,历史扩展不像以前那么有用——它仍然很有用,但没有它你也能过得去。

您可以通过设置histchars变量来更改哪个字符触发历史替换;如果您很少使用历史替换,您可以设置 eghistchars='¡^'以便¡触发历史扩展而不是!. 您甚至可以使用 完全关闭该功能set +o histexpand


Sté*_*las 5

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

histchars='!^ '
Run Code Online (Sandbox Code Playgroud)

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

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash
Run Code Online (Sandbox Code Playgroud)

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

如果您同时需要这两个功能,则需要选择另一个字符作为$histchars. 你想要一个不影响你的命令被解释的方式。几个选项:

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

    histchars='!^%'
    alias %=
    
    Run Code Online (Sandbox Code Playgroud)

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

    bash-4.3$ % echo !!
    !!
    
    Run Code Online (Sandbox Code Playgroud)

(您将无法不记录已禁用历史替换的命令。另请注意,默认的第 3 个字符$histchars是 ,#因此不会在注释中完成历史扩展。如果您更改它,并在提示,您应该知道 ! 序列可能会在那里扩展)。