9

The following code will cause the 'if' statement to exit early and not execute the 'echo' command in the 'if' block. I am wondering why this happens only in the 'if' block but not in the main part of the script. Note: I understand changing ':=' to ':-' will solve the problem - I am not looking to fix the problem, I am looking to understand the difference between the execution environment of the 'if' block that causes it to happen in the first place.

#!/bin/bash

if true; then
        VAR=${$1:='val'}
        echo "This does not run"
fi


VAR=${$1:='val'}
echo "This does run"

The output is

line 4: ${$1:='val'}: bad substitution
line 7: ${$1:='val'}: bad substitution
This does run

Again - I am not interested in fixing the bad substitution error message, I understand how to do that and why it happens. What I want to understand is why the echo "This does not run" line does not run when there is a bad substitution above it in an 'if' block.

Reproduced on the following bash versions:

GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)

GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

12
  • I can reproduce the behavior with GNU bash, version 5.1.16(1)-release (x86_64-pc-msys) and GNU bash, version 5.0.3(1)-release (arm-unknown-linux-gnueabihf)
    – Bodo
    Dec 16, 2022 at 16:49
  • Just so you know, you should post the error message too. Dec 16, 2022 at 17:09
  • @ConstantinHong thanks for the suggestion - in this case I am more interested in the output than the error but I added the output as suggested Dec 16, 2022 at 17:59
  • You should always post the errors, simply so we don't get distracted when we test and see "ooh, look, there's an error! Maybe that's the problem". So if you show it up front, you save everyone time.
    – terdon
    Dec 16, 2022 at 18:00
  • 1
    Here's something interesting: try putting the variable assignment in a subshell. Like this: if true; then ( VAR=${$1:='val'} ); echo "This does not run"; fi. That makes it run the echo.
    – terdon
    Dec 16, 2022 at 18:01

1 Answer 1

3

As Kusalananda pointed in comments, this is essentially the same as described in setting Bash options in a compound command.

From Bash reference:

The following is a brief description of the shell’s operation when it reads and executes a command. Basically, the shell does the following:

  1. Parses the tokens into simple and compound commands (see Shell Commands).

  2. Performs the various shell expansions (see Shell Expansions), breaking the expanded tokens into lists of filenames (see Filename Expansion) and commands and arguments.

  3. ...

  4. Executes the command (see Executing Commands).

The block in the if statement is a compound command and has an expansion error, namely VAR=${$1:='val'}, so the whole chunk fails, step 6 is never reached and the first echo is never executed.

Note that this particular handling of expansion errors violates the POSIX standard, that in section 2.8.1 says that an expansion error shall exit a non-interactive shell.

In POSIX mode, Bash does operate in a conformant manner:

% bash horse 
horse: line 2: ${$1:='val'}: bad substitution
horse: line 4: ${$1:='val'}: bad substitution
This does run
% bash --posix horse 
horse: line 2: ${$1:='val'}: bad substitution

To acquit Bash, this violation is documented:

The following list is what’s changed when ‘POSIX mode’ is in effect:

  1. A non-interactive shell exits with an error status if a variable assignment error occurs when no command name follows the assignment statements.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .