2

Ran into some unexpected but interesting behavior. I was doing a somewhat complex execution when I ran into a situation that from my understanding of how Bash handles characters shouldn't have happened.

Smaller example of issue:

$ arr=( "$(echo '1 && !/==/')" )
-bash: !/=: event not found

What is going on here? To my understanding, the single quotes should force Bash to interpret all the characters literally without doing any type of expansion.

Using Bash 4.1.2.

EDIT: Simplified question for reproduction.

Kusalananda
  • 333,661
AnthonyBB
  • 351
  • 1
    How about the single quote in "don't"? –  Feb 12 '21 at 23:58
  • I can't reproduce the problem (after removing the single-quote in don't). Can you post an exact example that shows the problem (along with the exact error message you get)? Also, what version of bash are you using (echo "$BASH_VERSION" will tell you)? – Gordon Davisson Feb 13 '21 at 00:43
  • Replace !/pat/ by (/pat/==0) so that atleast history expansion error goes away. Then see what you get. – guest_7 Feb 13 '21 at 00:45
  • I don't see any input for your awk command... which makes me believe that what you posted is not actually representative of your command. Please post an actual MWE. – steeldriver Feb 13 '21 at 01:06
  • I apologize for the original post not having all the available context, I ran into this issue at work and it bothered me through the weekend and I posted as much as I could remember thinking it was enough. – AnthonyBB Feb 16 '21 at 12:51
  • @AnthonyBB, if at all possible, please try to reduce commands like that to some smaller examples, such that don't depend on your specific case and your specific files. That makes it easier for others to test, when they don't have to worry about getting "file not found" errors or figuring what your script will do to their files... – ilkkachu Feb 16 '21 at 13:39

2 Answers2

3

bash exits with an error about a bad event

You have been bitten by the dreaded history substitution (the ! character inside your double quotes). You can disable it with set +H. That won't happen in a script BTW.

About word splitting: you can use readarray (or its alias mapfile)

readarray -t array < <(df -Ph ...)
xhienne
  • 17,793
  • 2
  • 53
  • 69
1

Smaller example (but probably not smallest), in Bash 4.1:

$ set -H
$ arr=( "$(echo '1 && !/==/' )" )
bash4.1: !/==/': event not found
$ arr=(  $(echo '1 && !/==/' )  )              # no error
$ 

and in Bash 4.4:

$ set -H
$ arr=( "$(echo '1 && !/==/' )" )              # no error
$ arr=(  $(echo '1 && !/==/' )  )              # no error
$ 

That's history expansion allright. It happens quite early in the command line processing, and doesn't parse the whole command before that:

History expansion is performed immediately after a complete line is read, before the shell breaks it into words, and is performed on each line individually.

No idea why it works like that, why the double quoting makes a difference, but it's not the only bug with history expansion I remember hearing about.

You can turn history expansion off with set +H, or set +o histexpand, and it's not enabled in scripts by default anyway.

Also note that Bash 4.1 is more than 10 years old now, even Bash 4.4 (the last with a 4.x version number) was released in 2016. Other bugs have been fixed since 4.1, too, as well as useful features added.

ilkkachu
  • 138,973
  • Yeah, I noticed that removing the double quotes removed the issue, but it doesn't stop Bash from doing word splitting, which I wanted to disable for this case. – AnthonyBB Feb 16 '21 at 13:42
  • 1
    @AnthonyBB, I know, it was really just to point out how fiddly it is, when such a minor change changes if it's recognized as history expansion. I think the real solution is to disable history expansion anyway. (Which I'd recommend even without issues like this, except for people who really like it.) Also, consider updating. – ilkkachu Feb 16 '21 at 13:46