How to escape history expansion exclamation mark ! inside a double quoted string?

后端 未结 4 516
心在旅途
心在旅途 2020-11-27 20:27

EDIT: the command substitution is not necessary for the surprising behavior, although it is the most common use case. The same question applies to just echo \"\'!b\'\"

相关标签:
4条回答
  • 2020-11-27 20:34

    Sometimes you need to make a small addition to a big command pipe

    The OP's "Good, but verbose" example is actually pretty awesome for many cases.

    Please forgive the contrived example. The whole reason I need such a solution is that I have a lot of distracting, nested code. But, it boils down to: I must do a !d in sed within a double quoted bash command expansion.

    This works

    $ ifconfig | sed '/inet/!d'
    inet 127.0.0.1 netmask 0xff000000
    …
    

    This does not

    $ echo "$(ifconfig | sed '/inet/!d')"
    -bash: !d': event not found
    

    This is a simplest compromise

    $ echo "$(ifconfig | sed '/inet/'\!'d')"
    inet 127.0.0.1 netmask 0xff000000
    …
    

    Using the compromise allows me to insert a few characters into the existing code and produce a Pull Request that anyone can understand… even though resulting code is more difficult to understand. If I did a complete refactor, the code reviewers would have a much more challenging time verifying it. And of course this bash has no unit tests.

    0 讨论(0)
  • 2020-11-27 20:42

    If History Expansion is enabled, you can only echo the ! character if it is put in single quotes, escaped or if followed by a whitespace character, carriage return, or =.

    From man bash:

       Only backslash (\) and single quotes can  quote  the  history
       expansion character.
    
       Several  characters inhibit history expansion if found immediately fol-
       lowing the history expansion character, even if it is unquoted:  space,
       tab,  newline,  carriage return, and =.
    

    I believe the key word here is “Only”. The examples provided in the question only consider the outer most quotes being double quotes.

    0 讨论(0)
  • 2020-11-27 20:45

    This was repeatedly reported as a bug, most recently against bash 4.3 in 2014, for behavior going back to bash 3.

    There was some discussion whether this constituted a bug or expected but perhaps undesirable behavior; it seems the consensus has been that, however you want to characterize the behavior, it shouldn't be allowed to continue.

    It's fixed in bash 4.4, echo "$(echo '!b')" doesn't expand, echo "'!b'" does, which I regard as proper behavior because the single quotes are shell syntax markers in the first example and not in the second.

    0 讨论(0)
  • 2020-11-27 20:52

    In your last example,

    echo "$(echo '!b')"
    

    the exclamation point is not single-quoted. Because history expansion occurs so early in the parsing process, the single quotes are just part of the double-quoted string; the parser hasn't recognized the command substitution yet to establish a new context where the single quotes would be quoting operators.

    To fix, you'll have to temporarily turn off history expansion:

    set +H
    echo "$(echo '!b')"
    set -H
    
    0 讨论(0)
提交回复
热议问题