如何逃避历史扩展感叹号!在一个双引号"命令替换,如"$(echo'!b')"?

Cir*_*四事件 16 syntax bash quotes

编辑:命令替换对于令人惊讶的行为不是必需的,尽管它是最常见的用例.同样的问题适用于echo "'!b'"

b=a

# Enable history substitution.
# This option is on by default on interactive shells.
set -H

echo '!b'
# Output: '!b'
# OK. Escaped by single quotes.

echo $(echo '!b')
# Output: '!b'
# OK. Escaped by single quotes.

echo "$(echo '$b')"
# Output: '$b'
# OK. Escaped by single quotes.

echo "$(echo '!b')"
# Output: history expands
# BAD!! WHY??
Run Code Online (Sandbox Code Playgroud)
  • 在最后一个例子中,逃脱的最佳方法是!什么?
  • 即使我使用单引号,为什么它没有逃脱echo "$(echo '$b')"呢?!和之间有什么区别$
  • 为什么echo $(echo '!b')(没有报价)有效?(由@MBlanc指出).

我更愿意这样做:

  • set +H因为我set -H之后需要维持shell状态

  • 反斜杠转义,因为我需要一个!,它必须在引号之外:

    echo "$(echo '\!a')"
    # '\!a'.
    # No good.
    
    echo "$(echo 'a '\!' b '\!' c')"
    # a ! b ! c
    # Good, but verbose.
    
    Run Code Online (Sandbox Code Playgroud)
  • echo $(echo '!b') (没有引号),因为命令可以返回空格.

版:

bash --version | head -n1
# GNU bash, version 4.2.25(1)-release (i686-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

che*_*ner 20

在你的上一个例子中,

echo "$(echo '!b')"
Run Code Online (Sandbox Code Playgroud)

感叹号不是单引号.因为历史扩展在解析过程的早期发生,所以单引号只是双引号字符串的一部分; 解析器尚未识别命令替换以建立新的上下文,其中单引号将引用运算符.

要修复,您必须暂时关闭历史记录扩展:

set +H
echo "$(echo '!b')"
set -H
Run Code Online (Sandbox Code Playgroud)

  • @CiroSantilli 不幸的是,操作的确切顺序分散在整个手册页中。在 HISTORY EXPANSION 下,提到了在读取行后立即发生历史扩展,但在发生任何分词之前。这意味着在识别行中的任何其他结构之前,`!b` 将被适当的命令替换。 (2认同)