745 lines
24 KiB
YAML
745 lines
24 KiB
YAML
name: "Error handling"
|
|
cases:
|
|
- name: "Fatal error (interactive/subshell)"
|
|
ignore_stderr: true
|
|
args: ["-i"]
|
|
stdin: |
|
|
echo "before expansion"
|
|
(echo "${non_existent_var:?missing parameter}")
|
|
echo "after expansion"
|
|
|
|
- name: "Fatal error (non-interactive/subshell)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before expansion"
|
|
(echo "${non_existent_var:?missing parameter}")
|
|
echo "after expansion"
|
|
|
|
- name: "Fatal error (interactive/command substitution)"
|
|
ignore_stderr: true
|
|
args: ["-i"]
|
|
stdin: |
|
|
echo "before expansion"
|
|
x=$(echo "${non_existent_var:?missing parameter}")
|
|
echo "after expansion"
|
|
|
|
- name: "Fatal error (non-interactive/command substitution)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before expansion"
|
|
x=$(echo "${non_existent_var:?missing parameter}")
|
|
echo "after expansion"
|
|
|
|
- name: "Fatal error (interactive/sourced file)"
|
|
ignore_stderr: true
|
|
args: ["-i"]
|
|
test_files:
|
|
- path: error.sh
|
|
contents: |
|
|
echo "before expansion"
|
|
echo "${non_existent_var:?missing parameter}"
|
|
echo "after expansion"
|
|
stdin: |
|
|
source error.sh
|
|
echo "after sourcing script with error"
|
|
|
|
- name: "Fatal error (non-interactive/sourced file)"
|
|
ignore_stderr: true
|
|
test_files:
|
|
- path: error.sh
|
|
contents: |
|
|
echo "before expansion"
|
|
echo "${non_existent_var:?missing parameter}"
|
|
echo "after expansion"
|
|
stdin: |
|
|
source error.sh
|
|
echo "after sourcing script with error"
|
|
|
|
- name: "Fatal error (interactive/executed script)"
|
|
ignore_stderr: true
|
|
args: ["-i", "error.sh"]
|
|
test_files:
|
|
- path: error.sh
|
|
contents: |
|
|
echo "before expansion"
|
|
echo "${non_existent_var:?missing parameter}"
|
|
echo "after expansion"
|
|
|
|
- name: "Fatal error (non-interactive/executed script)"
|
|
ignore_stderr: true
|
|
args: ["error.sh"]
|
|
test_files:
|
|
- path: error.sh
|
|
contents: |
|
|
echo "before expansion"
|
|
echo "${non_existent_var:?missing parameter}"
|
|
echo "after expansion"
|
|
|
|
- name: "Shell language syntax error (command line)"
|
|
incompatible_os: ["azurelinux"] # Needs investigation
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
[[ -% 3 ]]
|
|
echo "after syntax error"
|
|
|
|
- name: "Shell language syntax error (non-interactive)"
|
|
incompatible_os: ["azurelinux"] # Needs investigation
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -% 3 ]]
|
|
echo "after syntax error"
|
|
|
|
- name: "Shell language syntax error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -% 3 ]]
|
|
echo "after syntax error"
|
|
|
|
- name: "Special built-in error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before shift"
|
|
set -- foo
|
|
shift 2
|
|
echo "after shift"
|
|
|
|
- name: "Special built-in error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before shift"
|
|
set -- foo
|
|
shift 2
|
|
echo "after shift"
|
|
|
|
- name: "Special built-in error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before shift"
|
|
set -- foo
|
|
shift 2
|
|
echo "after shift"
|
|
|
|
- name: "Regular utility error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before cat"
|
|
cat /no/such/utility/path 2>/dev/null
|
|
echo "after cat (status $?)"
|
|
|
|
- name: "Regular utility error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before cat"
|
|
cat /no/such/utility/path 2>/dev/null
|
|
echo "after cat (status $?)"
|
|
|
|
- name: "Regular utility error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before cat"
|
|
cat /no/such/utility/path 2>/dev/null
|
|
echo "after cat (status $?)"
|
|
|
|
- name: "Special built-in redirection error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before special redirection"
|
|
: > missing_special_dir/out.txt
|
|
echo "after special redirection"
|
|
|
|
- name: "Special built-in redirection error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before special redirection"
|
|
: > missing_special_dir/out.txt
|
|
echo "after special redirection"
|
|
|
|
- name: "Special built-in redirection error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before special redirection"
|
|
: > missing_special_dir/out.txt
|
|
echo "after special redirection"
|
|
|
|
- name: "Compound command redirection error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before compound redirection"
|
|
if true; then echo inside; fi > missing_compound_dir/out.txt
|
|
echo "after compound redirection"
|
|
|
|
- name: "Compound command redirection error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before compound redirection"
|
|
if true; then echo inside; fi > missing_compound_dir/out.txt
|
|
echo "after compound redirection"
|
|
|
|
- name: "Compound command redirection error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before compound redirection"
|
|
if true; then echo inside; fi > missing_compound_dir/out.txt
|
|
echo "after compound redirection"
|
|
|
|
- name: "Function redirection error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
demo_func() { echo inside; }
|
|
echo "before function redirection"
|
|
demo_func > missing_function_dir/out.txt
|
|
echo "after function redirection"
|
|
|
|
- name: "Function redirection error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
demo_func() { echo inside; }
|
|
echo "before function redirection"
|
|
demo_func > missing_function_dir/out.txt
|
|
echo "after function redirection"
|
|
|
|
- name: "Function redirection error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
demo_func() { echo inside; }
|
|
echo "before function redirection"
|
|
demo_func > missing_function_dir/out.txt
|
|
echo "after function redirection"
|
|
|
|
- name: "Utility redirection error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before cat redirection"
|
|
cat < missing_utility_dir/input.txt
|
|
echo "after cat redirection"
|
|
|
|
- name: "Utility redirection error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before cat redirection"
|
|
cat < missing_utility_dir/input.txt
|
|
echo "after cat redirection"
|
|
|
|
- name: "Utility redirection error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before cat redirection"
|
|
cat < missing_utility_dir/input.txt
|
|
echo "after cat redirection"
|
|
|
|
- name: "Variable assignment error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
readonly value_guard=1
|
|
echo "before readonly assignment"
|
|
value_guard=2
|
|
echo "after readonly assignment"
|
|
|
|
- name: "Variable assignment error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly value_guard=1
|
|
echo "before readonly assignment"
|
|
value_guard=2
|
|
echo "after readonly assignment"
|
|
|
|
- name: "A special builtin named by an expansion keeps its redirection error fatal in POSIX mode"
|
|
known_failure: true # TODO(posix): a special builtin's failed redirection doesn't end the shell when an expansion names the command
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
cmd=:
|
|
$cmd > /nonexistent-dir/file
|
|
echo "not reached"
|
|
|
|
- name: "local of a readonly variable fails"
|
|
known_failure: true # TODO(local): local shadows a readonly variable rather than failing with status 1
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
f() { local r=2; echo "in f: $r"; }
|
|
f
|
|
echo "f: $?"
|
|
|
|
- name: "A special builtin's misuse in the EXIT trap sets the shell's status in POSIX mode"
|
|
known_failure: true # TODO(posix): the EXIT trap's misuse leaves the shell's status as it was
|
|
ignore_stderr: true
|
|
stdin: |
|
|
(set -o posix; trap "set -o bogus" EXIT; echo "in subshell")
|
|
echo "after subshell: $?"
|
|
|
|
- name: "Variable assignment error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly value_guard=1
|
|
echo "before readonly assignment"
|
|
value_guard=2
|
|
echo "after readonly assignment"
|
|
|
|
- name: "Expansion error (command line)"
|
|
known_failure: true # Needs investigation
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before expansion"
|
|
echo "${non_existent_var:?missing parameter}"
|
|
echo "after expansion"
|
|
|
|
- name: "Expansion error (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before expansion"
|
|
echo "${UNSET_REQUIRED:?missing parameter}"
|
|
echo "after expansion"
|
|
|
|
- name: "Expansion error (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before expansion"
|
|
echo "${UNSET_REQUIRED:?missing parameter}"
|
|
echo "after expansion"
|
|
|
|
- name: "Command not found (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
missing_command
|
|
echo "after missing command (status $?)"
|
|
|
|
- name: "Command not found (non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
missing_command
|
|
echo "after missing command (status $?)"
|
|
|
|
- name: "Command not found (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
missing_command
|
|
echo "after missing command (status $?)"
|
|
|
|
- name: "Unrecoverable read error (command line)"
|
|
ignore_stderr: true
|
|
args:
|
|
- "-c"
|
|
- |
|
|
echo "before closing stdin"
|
|
exec 0<&-
|
|
echo "after closing stdin"
|
|
|
|
- name: "Unrecoverable read error (non-interactive)"
|
|
known_failure: true
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before closing stdin"
|
|
exec 0<&-
|
|
echo "after closing stdin"
|
|
|
|
- name: "Unrecoverable read error (interactive)"
|
|
known_failure: true
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before closing stdin"
|
|
exec 0<&-
|
|
echo "after closing stdin"
|
|
|
|
- name: "Failing special built-in (non-POSIX mode, non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
. ./non-existent-file
|
|
echo "after builtin call (status $?)"
|
|
|
|
- name: "Failing special built-in (non-POSIX mode, interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
. ./non-existent-file
|
|
echo "after builtin call (status $?)"
|
|
|
|
- name: "Failing special built-in (POSIX mode, non-interactive)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
. ./non-existent-file
|
|
echo "after builtin call (status $?)"
|
|
|
|
- name: "Failing special built-in (POSIX mode, interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
. ./non-existent-file
|
|
echo "after builtin call (status $?)"
|
|
|
|
- name: "A special builtin's misuse doesn't end a POSIX shell when its status is tested, and an assignment error does"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
(set -o bogus || echo "ran ||"; echo "survived set -o bogus ||")
|
|
(if shift x; then :; fi; echo "survived if shift x")
|
|
(readonly r=1; readonly r=2 || echo "ran ||"; echo "survived readonly r=2 ||")
|
|
(. /nonexistent-file || echo "ran ||"; echo "survived . ||")
|
|
|
|
- name: "Prefix assignment to a readonly variable (POSIX mode, non-interactive)"
|
|
known_failure: true # in POSIX mode, bash doesn't run the command; brush does
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
readonly R=1
|
|
R=2 printf 'ran\n'
|
|
echo "after"
|
|
|
|
- name: "errexit with failing command (non-interactive)"
|
|
stdin: |
|
|
set -e
|
|
false
|
|
echo "after false"
|
|
|
|
- name: "errexit with failing command (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -e
|
|
echo "before false"
|
|
false
|
|
echo "after false"
|
|
|
|
- name: "errexit with unbound variable access (interactive)"
|
|
args: ["-i", "-e", "-u", "-o", "pipefail"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "before false"
|
|
false
|
|
echo "after false"
|
|
|
|
# Only the presence of escape sequences is compared, not the (differing) wording. The harness
|
|
# passes --disable-color by default, which would hide the behavior under test.
|
|
- name: "Error messages are uncolored when stderr is not a terminal"
|
|
known_failure: true # brush colors its error messages unless given --disable-color
|
|
removed_default_args: ["--disable-color"]
|
|
stdin: |
|
|
out=$(missing_command 2>&1)
|
|
[[ $out == *$'\e'* ]] && echo "command not found: colored" || echo "command not found: plain"
|
|
out=$(cd /no/such/dir 2>&1)
|
|
[[ $out == *$'\e'* ]] && echo "builtin error: colored" || echo "builtin error: plain"
|
|
|
|
- name: "Expansion error in a redirected group"
|
|
known_failure: true # TODO(errors): the error leaves the group before it's shown, so it's shown on the shell's stderr despite the group's redirect, and the shell exits with status 1 rather than 127
|
|
stdin: |
|
|
unset zz
|
|
{ echo "${zz?unset}"; } 2>/dev/null
|
|
echo "not reached"
|
|
|
|
- name: "Builtin diagnostics carry the script location"
|
|
known_failure: true # TODO(errors): brush prints no "file: line N:" location
|
|
stdin: |
|
|
export 1x=2
|
|
|
|
- name: "Builtin usage errors are reported without an error prefix"
|
|
known_failure: true # TODO(errors): brush's error formatter prints "error: " and no location
|
|
stdin: |
|
|
{ export -Z; } 2>&1 | head -n 1
|
|
|
|
- name: "Builtin I/O errors are reported as bash reports them"
|
|
known_failure: true # TODO(errors): brush reports "i/o error: ... (os error 2)"
|
|
stdin: |
|
|
cd /nonexistent-dir
|
|
|
|
# The leading "file: line N:" a shell prefixes is stripped.
|
|
- name: "Parameter expansion errors name the parameter"
|
|
known_failure: true # TODO(errors): brush reports "expansion error: <message>" or "expanding unset variable: ${name}"
|
|
stdin: |
|
|
strip() { sed -E 's/^[^ ]*: line [0-9]+: //'; }
|
|
unset y zz arr
|
|
(echo ${y:?}) 2>&1 | strip
|
|
(echo ${y?}) 2>&1 | strip
|
|
(echo ${y:?custom message}) 2>&1 | strip
|
|
(set -u; echo $zz) 2>&1 | strip
|
|
(set -u; echo ${arr[1]}) 2>&1 | strip
|
|
|
|
- name: "Arithmetic errors name the expression and the token at fault"
|
|
known_failure: true # TODO(errors): brush reports "arithmetic evaluation error: <message>" without the expression
|
|
min_oracle_version: "5.3" # bash 5.2 says "syntax error", not "arithmetic syntax error"
|
|
stdin: |
|
|
strip() { sed -E 's/^[^ ]*: line [0-9]+: //'; }
|
|
(echo $((1/0))) 2>&1 | strip
|
|
(echo $((5%0))) 2>&1 | strip
|
|
(echo $((1+))) 2>&1 | strip
|
|
((1/0)) 2>&1 | strip
|
|
|
|
- name: "Special builtin runtime error is fatal in POSIX mode"
|
|
min_oracle_version: "5.3" # bash 5.2 reports the error and goes on
|
|
known_failure: true # TODO(posix): this special builtin error doesn't end a non-interactive POSIX shell
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
export 1x=2
|
|
echo "after export: $?"
|
|
|
|
- name: "Unsetting a readonly variable is fatal in POSIX mode"
|
|
min_oracle_version: "5.3" # bash 5.2 reports the error and goes on
|
|
ignore_stderr: true
|
|
stdin: |
|
|
(set -o posix; readonly rx=1; unset rx; echo "after unset: $?"); echo "unset: $?"
|
|
readonly ry=1
|
|
unset ry
|
|
echo "outside POSIX mode: $?"
|
|
|
|
- name: "unset -v's invalid name is fatal in POSIX mode, and plain unset's isn't"
|
|
known_failure: true # TODO(unset): an invalid name is accepted silently, so it can't end the shell
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
(unset -v 1x; echo "survived: $?"); echo "unset -v 1x: $?"
|
|
(unset -v 'a[1'; echo "survived: $?"); echo "unset -v 'a[1': $?"
|
|
(unset 1x; echo "survived: $?"); echo "unset 1x: $?"
|
|
(unset -f 1x; echo "survived: $?"); echo "unset -f 1x: $?"
|
|
|
|
- name: "Builtin errors in a pipeline are reported on the command's stderr"
|
|
known_failure: true # TODO(pipeline): a builtin's error in a stage escapes the stage's stderr
|
|
stdin: |
|
|
strip() { sed -E 's/\x1b\[[0-9;]*m//g; s/^[^ ]*: line [0-9]+: //; s/^error: //'; }
|
|
shopt -s -u extglob 2>&1 | strip
|
|
echo "shopt: ${PIPESTATUS[*]}"
|
|
export -Z 2>/dev/null | cat
|
|
echo "export: ${PIPESTATUS[*]}"
|
|
cd /nonexistent-dir 2>/dev/null | cat
|
|
echo "cd: ${PIPESTATUS[*]}"
|
|
true | cd /nonexistent-dir 2>/dev/null
|
|
echo "last stage: ${PIPESTATUS[*]}"
|
|
|
|
- name: "A special builtin's usage and assignment errors are fatal in POSIX mode"
|
|
known_failure: true # TODO(posix): a special builtin's usage and assignment errors don't end a non-interactive POSIX shell
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
(set -o bogus; echo "survived: $?")
|
|
echo "status $?: set -o bogus"
|
|
(set --bogus; echo "survived: $?")
|
|
echo "status $?: set --bogus"
|
|
(trap -Z; echo "survived: $?")
|
|
echo "status $?: trap -Z"
|
|
(shift abc; echo "survived: $?")
|
|
echo "status $?: shift abc"
|
|
(readonly 1x; echo "survived: $?")
|
|
echo "status $?: readonly 1x"
|
|
(export 1x=2 y=3; echo "survived: $?")
|
|
echo "status $?: export 1x=2 y=3"
|
|
(readonly r=1; readonly r=2; echo "survived: $?")
|
|
echo "status $?: readonly r=1; readonly r=2"
|
|
(readonly r=1; unset r; echo "survived: $?")
|
|
echo "status $?: readonly r=1; unset r"
|
|
(readonly r=1; export r=2; echo "survived: $?")
|
|
echo "status $?: readonly r=1; export r=2"
|
|
(. /nonexistent-file; echo "survived: $?")
|
|
echo "status $?: . /nonexistent-file"
|
|
(f() { return abc; }; f; echo "survived: $?")
|
|
echo "status $?: f() { return abc; }; f"
|
|
(return 3; echo "survived: $?")
|
|
echo "status $?: return 3"
|
|
(exec -Z; echo "survived: $?")
|
|
echo "status $?: exec -Z"
|
|
(export --help >/dev/null; echo "survived: $?")
|
|
echo "status $?: export --help >/dev/null"
|
|
(for i in 1; do break abc; done; echo "survived: $?")
|
|
echo "status $?: for i in 1; do break abc; done"
|
|
|
|
- name: "A special builtin's ordinary failures are not fatal in POSIX mode"
|
|
known_failure: true # TODO(posix): any error a special builtin returns ends a non-interactive POSIX shell
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
for cmd in 'trap x BOGUS' 'set -- a; shift 3' 'export -f nofunc' 'unset -f -v x' \
|
|
'unset 1x' 'for i in 1; do break 0; done' 'break' 'continue 2'; do
|
|
(eval "$cmd"; echo "survived $?: $cmd")
|
|
done
|
|
|
|
- name: "A failed exec ends a non-interactive shell unless execfail is set"
|
|
known_failure: true # TODO(exec): a failed exec doesn't end a non-interactive shell
|
|
ignore_stderr: true
|
|
stdin: |
|
|
(exec /nonexistent-command; echo "survived")
|
|
echo "missing: $?"
|
|
(exec /etc/passwd; echo "survived")
|
|
echo "not executable: $?"
|
|
(shopt -s execfail; exec /nonexistent-command; echo "survived execfail: $?")
|
|
|
|
- name: "A special builtin's redirection error is fatal in POSIX mode"
|
|
known_failure: true # TODO(posix): the shell reports a redirection error before it knows the command
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
exec 3</nonexistent-file
|
|
echo "survived: $?"
|
|
|
|
- name: "Builtins attribute their diagnostics as bash does"
|
|
known_failure: true # TODO(errors): some builtins print diagnostics without bash's attribution or wording
|
|
stdin: |
|
|
strip() { sed -E 's/\x1b\[[0-9;]*m//g; s/^[^ ]*: line [0-9]+: //; s/^error: //'; }
|
|
enable nosuch 2>&1 | strip
|
|
(unset HOME; cd) 2>&1 | strip
|
|
[ 1 = 1 2>&1 | strip
|
|
help nosuchtopic 2>&1 | strip
|
|
kill -s BOGUS $$ 2>&1 | strip
|
|
type nosuch 2>&1 | strip
|
|
unalias nosuch 2>&1 | strip
|
|
|
|
- name: "An assignment error drops the rest of the line"
|
|
known_failure: true # TODO(errors): runs the rest of the line
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
r=2; echo "same line: $?"
|
|
echo "next line: $?"
|
|
f() { r=3; }; f; echo "same line after f: $?"
|
|
echo "next line after f: $?"
|
|
|
|
# An error that bash abandons the current line for stays that way when the command it happens
|
|
# in is a compound command with redirections: the redirections only decide where the message
|
|
# goes (the cases above and below check that), not what the error does to the enclosing list.
|
|
- name: "An error in a redirected group drops the rest of the line"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
{ r=2; } 2>/dev/null; echo "same line after assignment: $?"
|
|
echo "next line: $?"
|
|
{ echo $((1/0)); } 2>/dev/null; echo "same line after arithmetic: $?"
|
|
echo "next line: $?"
|
|
shopt -s failglob
|
|
{ echo /no-such-dir-*; } 2>/dev/null; echo "same line after failglob: $?"
|
|
echo "next line: $?"
|
|
|
|
- name: "An error in a redirected compound command drops the rest of the line"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
if true; then r=2; fi 2>/dev/null; echo "same line after if: $?"
|
|
echo "next line: $?"
|
|
for i in 1; do r=2; done 2>/dev/null; echo "same line after for: $?"
|
|
echo "next line: $?"
|
|
while true; do r=2; done 2>/dev/null; echo "same line after while: $?"
|
|
echo "next line: $?"
|
|
|
|
- name: "An error in a redirected group isn't a failure for ||"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
{ r=2; } 2>/dev/null || echo "ran ||"; echo "same line"
|
|
echo "next line"
|
|
|
|
- name: "An error in a redirected group isn't a false condition"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
if { r=2; } 2>/dev/null; then echo "then"; else echo "else"; fi; echo "same line after if"
|
|
echo "next line"
|
|
! { r=2; } 2>/dev/null && echo "negated"; echo "same line after !"
|
|
echo "next line"
|
|
|
|
- name: "An error in a redirected group abandons the enclosing loop"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
for i in 1 2 3; do { r=2; } 2>/dev/null; echo "iteration $i"; done
|
|
echo "after for"
|
|
i=0
|
|
until { r=2; } 2>/dev/null; do i=$((i + 1)); [ $i -ge 3 ] && break; echo "tick $i"; done
|
|
echo "after until"
|
|
i=0
|
|
while true; do { r=2; } 2>/dev/null || break; i=$((i + 1)); [ $i -ge 3 ] && break; done; echo "same line after while"
|
|
echo "after while"
|
|
|
|
- name: "An error in a redirected group abandons the function it's in"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
f() { { r=2; } 2>/dev/null; echo "still in f"; }
|
|
f
|
|
echo "next line"
|
|
|
|
- name: "An error in a redirected group doesn't run the ERR trap"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
trap 'echo "ERR trap"' ERR
|
|
readonly r=1
|
|
{ r=2; } 2>/dev/null; echo "same line"
|
|
echo "next line"
|
|
|
|
# Not observable as a shell exit yet: an assignment error doesn't end a POSIX shell (see
|
|
# "A special builtin's usage and assignment errors are fatal in POSIX mode"), so this only
|
|
# checks that the rest of the line is still dropped.
|
|
- name: "An error in a redirected group drops the rest of the line (POSIX mode)"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
readonly r=1
|
|
{ r=2; } 2>/dev/null; echo "same line"
|
|
|
|
- name: "A fatal error in a redirected group drops the rest of the line (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
unset zz
|
|
{ echo "${zz?missing parameter}"; } 2>/dev/null; echo "same line"
|
|
echo "next line"
|
|
|
|
- name: "An error in a redirected group drops the rest of the line (interactive)"
|
|
args: ["-i"]
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly r=1
|
|
{ r=2; } 2>/dev/null; echo "same line"
|
|
echo "next line"
|
|
|
|
- name: "A syntax error in a sourced file fails the source and goes on"
|
|
known_failure: true # TODO(errors): the syntax error ends the shell
|
|
ignore_stderr: true
|
|
test_files:
|
|
- path: "bad.sh"
|
|
contents: |
|
|
echo )
|
|
stdin: |
|
|
. ./bad.sh
|
|
echo "after: $?"
|
|
|
|
- name: "A syntax error in a sourced file ends a POSIX shell"
|
|
ignore_stderr: true
|
|
test_files:
|
|
- path: "bad.sh"
|
|
contents: |
|
|
echo )
|
|
stdin: |
|
|
set -o posix
|
|
. ./bad.sh
|
|
echo "after: $?"
|
|
|