Shell behavior difference with empty parameter in heredocument
Asked Answered
A

2

8

Ash, Dash or Bash, Zsh handles invalid empty parameters ${var:?} expansion error in a here-document differently.

Here is the experiment.sh code using genuine POSIX grammar:

#!/usr/bin/env sh

empty_var=

read -r value << EOF
Expected fail here ${empty_var:?}"
EOF
printf '$?=%d\nvalue=%s\n' $? "$value"

And here is the code to run the experiment with different shells:

for sh in ash bash dash ksh zsh; do
  printf 'Testing with: %s\n' "$sh"
  LC_ALL=C "$sh" ./experiment.sh || :
  echo
done

Obtained results:

Testing with: ash
./experiment.sh: 5: empty_var: parameter not set or null
$?=2
value=

Testing with: bash
./experiment.sh: line 5: empty_var: parameter null or not set

Testing with: dash
./experiment.sh: 5: empty_var: parameter not set or null
$?=2
value=

Testing with: ksh
./experiment.sh[5]: empty_var: parameter null
$?=1
value=

Testing with: zsh
./experiment.sh:5: empty_var: parameter not set

Bash and Zsh stops execution immediately while other shells continue the execution, just raising the return code $?.

What explanations are there, for this behavior difference with this genuine POSIX grammar construct?

Is it documented why Bash or Zsh choose to exit the script rather than return a failure code like Ksh, Dash or Ash?

Note that within other contexts like expansion in a string, all shells exit out of the script.

The behavior discrepancy occurs only in here-documents as far as I know.

Atombomb answered 24/3, 2023 at 16:18 Comment(1)
Switch to $sh -i ./experiment.sh, and you'll see the behaviors of most shells other than zsh line up. To get zsh to exhibit the same non-exit behavior as others, use $sh -is < ./experiment.sh. dash, bash, zsh, oksh, ksh93, and BusyBox ash all continue execution when using you use -is and input redirection. Even if you dislike having to handle the issue where shells continue executing the script, it means those shells are consistent, which is easy enough to handle. Of course, you could simply not use the ${parameter:?[word]} form.Weepy
F
4

The relevant POSIX documentation seems to be in the Parameter Expansion section of the Open Group Shell Command Language document.

${parameter:?[word]}
Indicate Error if Null or Unset. If parameter is unset or null, the expansion of word (or a message indicating it is unset if word is omitted) shall be written to standard error and the shell exits with a non-zero exit status. Otherwise, the value of parameter shall be substituted. An interactive shell need not exit.

My reading of it is that non-interactive shells are required to exit immediately. I can't find anything to suggest that it should behave differently in here-documents. It looks like you've found a bug that affects multiple shells.

Flywheel answered 27/3, 2023 at 20:12 Comment(0)
W
2

For bash, it's documented here :

  • ${parameter:?word}

    If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.

here is zsh documentation.

Wordplay answered 24/3, 2023 at 23:57 Comment(1)
Sorry I had to edit my post to make it more clear. I am curious about the behaviour differences between shell. Is it something that is undefined in POSIX or is one of the behavior more conform to POSIX, if so which one? Stop script or return error?Slink

© 2022 - 2024 — McMap. All rights reserved.