Merge nucleic/sleek-thistle-egret-fyej into dev

This commit is contained in:
2026-07-18 05:19:31 -07:00
commit b6be87b72d
677 changed files with 102939 additions and 0 deletions
@@ -0,0 +1,84 @@
name: "BASH_XTRACEFD tests"
cases:
- name: "basic redirection to fd 3"
stdin: |
exec 3>trace.txt
BASH_XTRACEFD=3
set -x
echo "hello"
echo "world"
set +x
cat trace.txt
- name: "redirection takes effect immediately"
stdin: |
exec 3>trace.txt
set -x
echo "before setting BASH_XTRACEFD"
BASH_XTRACEFD=3
echo "after setting BASH_XTRACEFD"
set +x
echo "Content of trace file:"
cat trace.txt
- name: "unsetting BASH_XTRACEFD returns to stderr"
stdin: |
exec 3>trace.txt
BASH_XTRACEFD=3
set -x
echo "redirected"
unset BASH_XTRACEFD
echo "back to stderr"
set +x
echo "Content of trace file:"
cat trace.txt
- name: "invalid fd value falls back to stderr"
known_failure: true # bash validates BASH_XTRACEFD at assignment time, brush doesn't have that mechanism
stdin: |
BASH_XTRACEFD=999
set -x
echo "should go to stderr"
set +x
- name: "non-numeric value falls back to stderr"
known_failure: true # bash validates BASH_XTRACEFD at assignment time, brush doesn't have that mechanism
stdin: |
BASH_XTRACEFD=invalid
set -x
echo "should go to stderr"
set +x
- name: "empty value falls back to stderr"
stdin: |
BASH_XTRACEFD=
set -x
echo "should go to stderr"
- name: "redirect to file that fails writes"
stdin: |
# This test relies on /dev/full being present. We degrade gracefully
# if it doesn't.
[[ -e /dev/full ]] || exit
exec 3>/dev/full
BASH_XTRACEFD=3
set -x
echo "first echo"
echo "first echo result: $?"
@@ -0,0 +1,8 @@
name: "Default options"
cases:
- name: "Default options"
stdin: |
echo "Default options: $-"
- name: "Default options (-c)"
args: ["-c", 'echo "Default options: $-"']
@@ -0,0 +1,213 @@
name: "Options: errtrace (-E)"
cases:
#
# Basic errtrace option behavior
#
- name: "errtrace disabled by default"
stdin: |
set +E 2>/dev/null || true
echo "errtrace test"
- name: "errtrace can be enabled and disabled"
stdin: |
set -E
echo "enabled: $-" | grep -q E && echo "E is set"
set +E
echo "disabled: $-" | grep -qv E && echo "E is unset"
#
# ERR trap inheritance in functions (core errtrace behavior)
#
- name: "errtrace disabled - functions don't inherit ERR trap"
stdin: |
set +E
trap 'echo "ERR trapped"' ERR
myfunc() { false; }
myfunc
echo "after"
- name: "errtrace enabled - functions inherit ERR trap"
stdin: |
set -E
trap 'echo "ERR trapped"' ERR
myfunc() { false; }
myfunc
echo "after"
- name: "errtrace with nested functions"
stdin: |
set -E
trap 'echo "ERR trapped"' ERR
inner() { false; }
outer() { inner; }
outer
echo "after"
- name: "errtrace toggling mid-script"
stdin: |
trap 'echo "ERR trapped"' ERR
set -E
myfunc1() { false; }
myfunc1
echo "after first call"
set +E
myfunc2() { false; }
myfunc2
echo "after second call"
#
# ERR trap inheritance in subshells
#
- name: "errtrace disabled - subshell doesn't inherit ERR trap"
stdin: |
set +E
trap 'echo "ERR"' ERR
(
trap -p ERR
false
echo "no ERR trap fired"
)
echo "after"
- name: "errtrace enabled - subshell inherits ERR trap"
stdin: |
set -E
trap 'echo "ERR"' ERR
(
trap -p ERR | grep -q ERR && echo "ERR trap inherited"
false
echo "after false in subshell"
)
echo "after"
#
# ERR trap inheritance in command substitution
#
- name: "errtrace disabled - command substitution doesn't inherit ERR trap"
stdin: |
set +E
trap 'echo "[ERR trapped]" >&2' ERR
result=$(trap -p ERR; false; echo "no trap inherited")
echo "result: $result"
- name: "errtrace enabled - command substitution inherits ERR trap"
stdin: |
set -E
trap 'echo "[ERR in subshell]" >&2' ERR
result=$(false; echo "after false in subshell")
echo "result: $result"
#
# ERR trap inheritance in pipelines
#
- name: "errtrace with pipeline - last command fails"
stdin: |
set -E
trap 'echo "[ERR: $?]" >&2' ERR
echo "data" | cat | false
echo "after pipeline"
- name: "errtrace with pipeline - middle command fails (pipefail)"
stdin: |
set -E
set -o pipefail
trap 'echo "[ERR: $?]" >&2' ERR
echo "data" | false | cat
echo "after pipeline"
- name: "errtrace - ERR trap in pipeline subshells"
stdin: |
set -E
trap 'echo "[ERR: $BASH_COMMAND]" >&2' ERR
echo "input" | { false; echo "after false in brace"; }
echo "done"
#
# ERR trap with errexit interaction
#
- name: "errtrace with errexit - ERR trap fires before exit"
stdin: |
set -e
set -E
trap 'echo "ERR trap fired"' ERR
false
echo "should not print"
- name: "errtrace with errexit in function"
known_failure: true # TODO(traps): ERR trap not firing in function with errexit
stdin: |
set -e
set -E
trap 'echo "ERR trap"' ERR
myfunc() { false; }
myfunc
echo "should not print"
#
# ERR trap inheritance in deeply nested functions
#
- name: "errtrace - ERR trap in deeply nested function"
stdin: |
set -E
trap 'echo "ERR at level ${#FUNCNAME[@]}"' ERR
level3() { false; }
level2() { level3; }
level1() { level2; }
level1
echo "after"
#
# ERR trap modification within functions
#
- name: "errtrace - ERR trap can be changed inside function"
stdin: |
set -E
trap 'echo "outer ERR"' ERR
myfunc() {
trap 'echo "inner ERR"' ERR
false
}
myfunc
false
echo "after"
- name: "errtrace - ERR trap can be cleared inside function"
stdin: |
set -E
trap 'echo "ERR"' ERR
myfunc() {
trap - ERR
false
echo "no ERR in func"
}
myfunc
false
echo "after"
#
# ERR trap in background jobs with errtrace
#
- name: "errtrace - ERR trap in background job"
stdin: |
set -E
trap 'echo "ERR" >&2' ERR
{ false; echo "bg done"; } &
wait
echo "main done"
#
# Sourced scripts with errtrace
#
- name: "errtrace - ERR trap inherited in sourced script"
test_files:
- path: "failing.sh"
contents: |
echo "in sourced"
false
echo "after false in sourced"
stdin: |
set -E
trap 'echo "ERR trapped"' ERR
. ./failing.sh
echo "after source"
@@ -0,0 +1,314 @@
name: "Options: extdebug"
cases:
#
# extdebug implies functrace (-T)
#
- name: "extdebug implies functrace - DEBUG inherited in functions"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
shopt -s extdebug
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
myfunc() {
echo "in func"
}
echo "before"
myfunc
echo "after"
- name: "extdebug implies functrace - DEBUG inherited in subshells"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
shopt -s extdebug
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
echo "before"
(echo "in subshell")
echo "after"
#
# DEBUG trap return value effects (extdebug-specific)
#
- name: "DEBUG trap - returns 0 command executes normally"
known_failure: true # TODO(extdebug): return in trap handler not supported
stdin: |
shopt -s extdebug
trap 'return 0' DEBUG
echo "line 1"
echo "line 2"
echo "line 3"
- name: "DEBUG trap - returns 1 command is skipped"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
skip_next=false
trap '
if $skip_next; then
skip_next=false
return 1
fi
return 0
' DEBUG
echo "line 1"
skip_next=true
echo "this should be skipped"
echo "line 3"
- name: "DEBUG trap - returns 2 simulates return from function"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap '
if [[ "${FUNCNAME[0]}" == "myfunc" && "$BASH_COMMAND" == *"should not"* ]]; then
return 2
fi
return 0
' DEBUG
myfunc() {
echo "start of func"
echo "this should not print"
echo "also should not print"
}
myfunc
echo "after func: $?"
- name: "DEBUG trap - return value ignored without extdebug"
known_failure: true # TODO(extdebug): return in trap handler error message differs
stdin: |
shopt -u extdebug
trap 'return 1' DEBUG
echo "line 1"
echo "line 2"
echo "line 3"
#
# Command skipping scenarios (extdebug-specific)
#
- name: "Skip simple command"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
echo "keep this"
echo "skip this"
echo "keep this too"
- name: "Skip in loop - iteration skipped but loop continues"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
for i in 1 2 3; do
echo "iter $i"
echo "skip $i"
echo "after skip $i"
done
echo "done"
- name: "Skip in conditional"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap '[[ "$BASH_COMMAND" == *"skip"* ]] && return 1; return 0' DEBUG
if true; then
echo "in then"
echo "skip in then"
echo "after skip in then"
fi
echo "after if"
- name: "Return 2 in nested function - unwinds to caller"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap '
if [[ "${FUNCNAME[0]}" == "inner" && "$BASH_COMMAND" == *"inner cmd"* ]]; then
return 2
fi
return 0
' DEBUG
inner() {
echo "inner start"
echo "inner cmd"
echo "inner end"
}
outer() {
echo "outer start"
inner
echo "outer after inner"
}
outer
echo "main after outer"
#
# BASH_ARGC and BASH_ARGV (extdebug-specific)
#
- name: "BASH_ARGC - contains argument counts per call frame"
stdin: |
shopt -s extdebug
show_argc() {
echo "BASH_ARGC: ${BASH_ARGC[*]}"
}
func_with_args() {
show_argc
}
func_with_args a b c
- name: "BASH_ARGV - contains arguments in reverse order"
stdin: |
shopt -s extdebug
show_argv() {
echo "BASH_ARGV: ${BASH_ARGV[*]}"
}
func_with_args() {
show_argv
}
func_with_args a b c
- name: "BASH_ARGC/ARGV - in nested calls"
stdin: |
shopt -s extdebug
inner() {
echo "inner ARGC: ${BASH_ARGC[*]}"
echo "inner ARGV: ${BASH_ARGV[*]}"
}
outer() {
inner x y
}
outer a b c
- name: "BASH_ARGC/ARGV - empty when no arguments"
stdin: |
shopt -s extdebug
no_args() {
echo "BASH_ARGC: '${BASH_ARGC[*]}'"
echo "BASH_ARGV: '${BASH_ARGV[*]}'"
}
no_args
#
# declare -F shows source info (extdebug-specific)
#
- name: "declare -F - shows function name only without extdebug"
stdin: |
myfunc() { echo hi; }
declare -F myfunc
- name: "declare -F - shows filename and line number with extdebug"
known_failure: true # TODO(extdebug): declare -F not showing source info
test_files:
- path: "funcs.sh"
contents: |
myfunc() {
echo hi
}
stdin: |
shopt -s extdebug
. ./funcs.sh
declare -F myfunc | sed 's/[0-9]*/N/'
#
# Interaction with other options
#
- name: "extdebug with errexit - skipped command doesn't trigger errexit"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
set -e
trap '[[ "$BASH_COMMAND" == "false" ]] && return 1; return 0' DEBUG
echo "before"
false
echo "after - should print because false was skipped"
- name: "extdebug disabled after being enabled"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
trap 'return 1' DEBUG
echo "line 1 - should be skipped"
shopt -u extdebug
echo "line 2 - should print"
echo "line 3 - should print"
- name: "extdebug with set -T - both affect DEBUG inheritance"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
shopt -s extdebug
set -T
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
myfunc() {
echo "in func"
}
myfunc
echo "done"
#
# DEBUG trap with extdebug in various contexts
#
- name: "DEBUG trap with extdebug in function"
known_failure: true # TODO(extdebug): return in trap handler not supported
stdin: |
shopt -s extdebug
count=0
trap '
((count++))
echo "debug $count: $BASH_COMMAND"
return 0
' DEBUG
myfunc() {
local x=1
echo "x=$x"
}
myfunc
- name: "DEBUG trap with extdebug controls command execution"
known_failure: true # TODO(extdebug): DEBUG trap return value not implemented
stdin: |
shopt -s extdebug
should_skip=false
trap '
if $should_skip; then
should_skip=false
echo "[skipping: $BASH_COMMAND]"
return 1
fi
return 0
' DEBUG
echo "first"
should_skip=true
echo "second - will be skipped"
echo "third"
#
# Edge cases
#
- name: "extdebug with recursive function"
known_failure: true # TODO(extdebug): DEBUG trap firing count differs
stdin: |
shopt -s extdebug
trap 'echo "depth: ${#FUNCNAME[@]}"' DEBUG
recurse() {
local n=$1
if ((n > 0)); then
recurse $((n - 1))
fi
}
recurse 3
- name: "BASH_COMMAND shows compound command"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
shopt -s extdebug
trap 'echo "cmd: $BASH_COMMAND"' DEBUG
if true; then echo "in if"; fi
- name: "DEBUG trap can modify variables"
known_failure: true # TODO(extdebug): return in trap handler not supported
stdin: |
shopt -s extdebug
counter=0
trap '((counter++)); return 0' DEBUG
echo "a"
echo "b"
echo "c"
echo "counter: $counter"
@@ -0,0 +1,314 @@
name: "Options: functrace (-T)"
cases:
#
# Basic functrace option behavior
#
- name: "functrace can be enabled and disabled"
stdin: |
set -T
echo "enabled: $-" | grep -q T && echo "T is set"
set +T
echo "disabled: $-" | grep -qv T && echo "T is unset"
#
# DEBUG trap inheritance in functions
#
- name: "DEBUG trap - does not fire in function without set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
myfunc() {
echo "in func"
}
echo "before func"
myfunc
echo "after func"
- name: "DEBUG trap - fires in function with set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
myfunc() {
echo "in func"
}
echo "before func"
myfunc
echo "after func"
#
# DEBUG trap inheritance in subshells
#
- name: "DEBUG trap - does not fire in subshell without set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
echo "before subshell"
(echo "in subshell")
echo "after subshell"
- name: "DEBUG trap - fires in subshell with set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
echo "before subshell"
(echo "in subshell")
echo "after subshell"
#
# DEBUG trap inheritance in command substitution
#
- name: "DEBUG trap - does not fire in command substitution without set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
echo "before"
result=$(echo "in substitution")
echo "result: $result"
- name: "DEBUG trap - fires in command substitution with set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
echo "before"
result=$(echo "in substitution")
echo "result: $result"
#
# DEBUG trap inheritance in pipelines
#
- name: "DEBUG trap - does not fire in pipeline without set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
echo "before"
echo "hello" | cat | cat
echo "after"
- name: "DEBUG trap - fires in pipeline with set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]" >&2' DEBUG
echo "before"
echo "hello" | cat | cat
echo "after"
#
# RETURN trap inheritance in functions
#
- name: "RETURN trap - does not fire in called function without set -T"
stdin: |
trap 'echo "[return: $FUNCNAME]"' RETURN
inner() {
echo "in inner"
}
outer() {
echo "in outer"
inner
echo "outer continuing"
}
outer
echo "after"
- name: "RETURN trap - fires in called function with set -T"
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
stdin: |
set -T
trap 'echo "[return: ${FUNCNAME:-main}]"' RETURN
inner() {
echo "in inner"
}
outer() {
echo "in outer"
inner
echo "outer continuing"
}
outer
echo "after"
- name: "RETURN trap - fires for each nested function return with set -T"
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
stdin: |
set -T
trap 'echo "[return: ${FUNCNAME:-main} -> $?]"' RETURN
level3() { echo "level3"; return 3; }
level2() { echo "level2"; level3; return 2; }
level1() { echo "level1"; level2; return 1; }
level1
echo "done"
#
# Function-specific trace attribute (declare -t)
#
- name: "declare -t - enables trace for specific function"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
traced_func() {
echo "in traced"
}
untraced_func() {
echo "in untraced"
}
declare -t traced_func
echo "calling untraced"
untraced_func
echo "calling traced"
traced_func
echo "done"
- name: "declare +t - disables trace for specific function"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
myfunc() {
echo "in func"
}
declare +t myfunc
echo "calling func"
myfunc
echo "done"
- name: "declare -f - shows trace attribute"
stdin: |
myfunc() { echo hi; }
declare -t myfunc
declare -f myfunc | head -1
#
# Toggling set -T mid-script
#
- name: "set -T enabled mid-script affects subsequent calls"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
func1() { echo "in func1"; }
func2() { echo "in func2"; }
echo "calling func1 without -T"
func1
set -T
echo "calling func2 with -T"
func2
echo "done"
- name: "set +T disables inheritance mid-script"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
func1() { echo "in func1"; }
func2() { echo "in func2"; }
echo "calling func1 with -T"
func1
set +T
echo "calling func2 without -T"
func2
echo "done"
#
# DEBUG trap inheritance in background jobs
#
- name: "DEBUG trap - in background job with set -T"
stdin: |
set -T
trap 'echo "[debug]" >&2' DEBUG
{
echo "in background"
} &
wait
echo "done"
#
# DEBUG trap inheritance in process substitution
#
- name: "DEBUG trap - in process substitution with set -T"
stdin: |
set -T
trap 'echo "[debug]" >&2' DEBUG
cat <(echo "from procsub")
echo "done"
#
# BASH_COMMAND visibility in DEBUG trap with functrace
#
- name: "BASH_COMMAND in function with set -T"
known_failure: true # TODO(BASH_COMMAND): quotes differ
stdin: |
set -T
trap 'echo "cmd: $BASH_COMMAND"' DEBUG
myfunc() {
local v=10
echo "v=$v"
}
myfunc
#
# Nested functions with varying trace states
#
- name: "Outer function traced, inner function not traced"
stdin: |
trap 'echo "[debug in ${FUNCNAME:-main}]"' DEBUG
inner() {
echo "inner"
}
outer() {
echo "outer start"
inner
echo "outer end"
}
declare -t outer
outer
echo "done"
- name: "Only inner function traced"
stdin: |
trap 'echo "[debug in ${FUNCNAME:-main}]"' DEBUG
inner() {
echo "inner"
}
outer() {
echo "outer start"
inner
echo "outer end"
}
declare -t inner
outer
echo "done"
#
# RETURN trap with sourced scripts and set -T
#
- name: "RETURN trap - fires for function in sourced script with set -T"
known_failure: true # TODO(functrace): RETURN trap inheritance not implemented
test_files:
- path: "sourced.sh"
contents: |
sourced_func() {
echo "in sourced_func"
}
sourced_func
stdin: |
set -T
trap 'echo "[return: ${FUNCNAME:-main}]"' RETURN
. ./sourced.sh
echo "after source"
#
# Edge cases
#
- name: "DEBUG trap - count in deeply nested function calls with set -T"
known_failure: true # TODO(functrace): DEBUG trap count differs
stdin: |
set -T
count=0
trap 'count=$((count + 1))' DEBUG
a() { b; }
b() { c; }
c() { echo "deep"; }
a
echo "debug count: $count"
@@ -0,0 +1,76 @@
name: "Options: inherit_errexit"
cases:
- name: "inherit_errexit disabled by default"
stdin: |
shopt -q inherit_errexit && echo "enabled" || echo "disabled"
- name: "inherit_errexit disabled - command substitution ignores errexit"
stdin: |
set -e
shopt -u inherit_errexit
result=$(false; echo "still running")
echo "result: $result"
echo "after"
- name: "inherit_errexit enabled - command substitution inherits errexit"
stdin: |
set -e
shopt -s inherit_errexit
result=$(false; echo "should not print")
echo "should not print"
- name: "inherit_errexit with nested command substitution"
stdin: |
set -e
shopt -s inherit_errexit
result=$(echo "outer: $(false; echo inner)")
echo "should not print"
- name: "inherit_errexit disabled - nested substitution continues"
stdin: |
set -e
shopt -u inherit_errexit
result=$(echo "outer: $(false; echo inner)")
echo "result: $result"
echo "after"
- name: "inherit_errexit with backtick substitution"
stdin: |
set -e
shopt -s inherit_errexit
result=`false; echo "should not print"`
echo "should not print"
- name: "inherit_errexit with assignment"
stdin: |
set -e
shopt -s inherit_errexit
var=$(false)
echo "should not print"
- name: "inherit_errexit in function"
stdin: |
set -e
shopt -s inherit_errexit
myfunc() {
local result=$(false; echo "should not print")
echo "after local"
}
myfunc
echo "should not print"
- name: "inherit_errexit toggling"
stdin: |
set -e
shopt -s inherit_errexit
result1=$(false; echo "should not print in first")
echo "should not print 1"
ignore_stderr: true
- name: "inherit_errexit with multiple commands in substitution"
stdin: |
set -e
shopt -u inherit_errexit
result=$(echo "start"; false; echo "after false")
echo "result: $result"
echo "after"
@@ -0,0 +1,9 @@
name: "set -B"
cases:
- name: "set -B"
stdin: |
set +B
echo "+B: " ${a,b}
set -B
echo "-B: " ${a,b}
@@ -0,0 +1,27 @@
name: "set -C"
cases:
- name: "set -C"
ignore_stderr: true
stdin: |
touch existing-file
set -C
echo hi > non-existing-file
echo "Result (non existing): $?"
echo "File contents: $(cat non-existing-file)"
echo
echo hi > /dev/null
echo "Result (device file): $?"
echo
echo hi > existing-file
echo "Result (existing file): $?"
echo "File contents: $(cat existing-file)"
echo
echo hi >| existing-file
echo "Result (clobber): $?"
echo "File contents: $(cat existing-file)"
echo
@@ -0,0 +1,20 @@
name: "set -a"
cases:
- name: "set -a"
stdin: |
v1=1 v2=2 v3=3 v4=4 v5=(a b c)
declare -p v1 v2 v3 v4
set -a
v1=reassigned
v2+=appended
declare -i v3
v4+=(appended)
v5[2]=updated
v6=new
v7[0]=new
v8=(new)
declare v9=new
declare -a v10=(new)
declare -p v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
name: "set -n"
cases:
- name: "set -n"
stdin: |
set -n
touch somefile.txt
ls
@@ -0,0 +1,7 @@
name: "set -t"
cases:
- name: "set -t"
args: ["-t"]
stdin: |
echo first
echo second
@@ -0,0 +1,534 @@
name: "Options: set -u"
cases:
# Basic behavior
- name: "Basic set variable access"
stdin: |
set -u
var="value"
echo "$var"
- name: "Unset variable triggers error"
ignore_stderr: true
stdin: |
set -u
echo "$unset_var"
echo "should not print"
- name: "Command line -u"
args: ["-u"]
ignore_stderr: true
stdin: |
echo "$unset_var"
echo "should not print"
# Special parameters - should NOT error even when unset
- name: "Special parameter $@ does not error"
stdin: |
set -u
echo "args: $@"
- name: "Special parameter $* does not error"
stdin: |
set -u
echo "args: $*"
- name: "Special parameter $# does not error"
stdin: |
set -u
echo "count: $#"
- name: "Special parameter $? does not error"
stdin: |
set -u
true
echo "status: $?"
- name: "Special parameter $$ does not error"
stdin: |
set -u
echo "pid: $$" > /dev/null
echo "ok"
- name: "Special parameter $! does not error when no background jobs"
known_failure: true
ignore_stderr: true
stdin: |
set -u
echo "last bg: ${!}" || echo "ok"
- name: "Special parameter $0 does not error"
stdin: |
set -u
echo "shell: $0" > /dev/null
echo "ok"
- name: "Special parameter $- does not error"
stdin: |
set -u
echo "flags: $-" > /dev/null
echo "ok"
# Positional parameters
- name: "Unset positional parameter triggers error"
ignore_stderr: true
stdin: |
set -u
myfunc() {
echo "$1"
}
myfunc
echo "should not print"
- name: "Set positional parameter does not error"
stdin: |
set -u
myfunc() {
echo "$1"
}
myfunc "arg"
- name: "Positional parameters beyond provided args error"
ignore_stderr: true
stdin: |
set -u
myfunc() {
echo "$2"
}
myfunc "only_one_arg"
echo "should not print"
# Parameter expansion forms
- name: "Unset variable with default does not error"
stdin: |
set -u
echo "${unset_var:-default}"
- name: "Empty vs unset with :- operator"
stdin: |
set -u
empty=""
echo "${empty:-default}"
echo "${unset:-default}"
- name: "Empty vs unset with - operator"
stdin: |
set -u
empty=""
echo "${empty-default}"
echo "${unset-default}"
# Arrays (if supported)
- name: "Unset array element with default"
stdin: |
set -u
arr=(a b c)
echo "${arr[10]:-default}"
- name: "Unset array treated as unset variable"
stdin: |
set -u
echo "${unset_array[@]}"
echo "should not print"
- name: "Unset array with default does not error"
stdin: |
set -u
echo "${unset_array[@]:-default}"
# Conditionals
- name: "Unset variable in conditional test errors"
ignore_stderr: true
stdin: |
set -u
if [ "$unset_var" = "value" ]; then
echo "then"
fi
echo "should not print"
- name: "Unset variable in [[ test errors"
ignore_stderr: true
stdin: |
set -u
if [[ $unset_var = "value" ]]; then
echo "then"
fi
echo "should not print"
- name: "Unset variable with default in conditional works"
stdin: |
set -u
if [ "${unset_var:-}" = "" ]; then
echo "empty"
fi
- name: "test -v for unset variable"
stdin: |
set -u
if test -v unset_var; then
echo "set"
else
echo "unset"
fi
- name: "test -z with unset variable errors"
ignore_stderr: true
stdin: |
set -u
if test -z "$unset_var"; then
echo "empty"
fi
echo "should not print"
# Command substitution
- name: "Unset variable in command substitution errors"
ignore_stderr: true
stdin: |
set -u
result=$(echo "$unset_var")
echo "should not print"
- name: "Unset variable with default in command substitution"
stdin: |
set -u
result=$(echo "${unset_var:-default}")
echo "$result"
# Arithmetic expansion
- name: "Unset variable in arithmetic defaults to zero"
ignore_stderr: true
stdin: |
set -u
result=$((unset_var + 5))
echo "$result"
# set +u to disable
- name: "set +u disables unset variable check"
stdin: |
set -u
set +u
echo "$unset_var"
echo "ok"
- name: "set -u can be re-enabled"
ignore_stderr: true
stdin: |
set -u
set +u
echo "$unset_var"
set -u
echo "$another_unset"
echo "should not print"
- name: "set +u in function does not affect parent"
stdin: |
set -u
myfunc() {
set +u
echo "$unset_in_func"
}
myfunc
echo "$unset_in_parent"
echo "should not print"
# Functions
- name: "Unset variable in function body errors"
ignore_stderr: true
stdin: |
set -u
myfunc() {
echo "$unset_var"
}
myfunc
echo "should not print"
- name: "Local unset variable errors"
ignore_stderr: true
stdin: |
set -u
myfunc() {
local unset_local
echo "$unset_local"
}
myfunc
echo "should not print"
- name: "Local variable declaration with value works"
stdin: |
set -u
myfunc() {
local my_var="value"
echo "$my_var"
}
myfunc
- name: "Function parameter shadows unset global"
stdin: |
set -u
myfunc() {
local var="$1"
echo "$var"
}
myfunc "arg"
# Subshells
- name: "Unset variable in subshell errors"
ignore_stderr: true
stdin: |
set -u
(echo "$unset_var")
echo "should not print"
- name: "set +u in subshell does not affect parent"
ignore_stderr: true
stdin: |
set -u
(
set +u
echo "$unset_in_subshell"
)
echo "$unset_in_parent"
echo "should not print"
# Variable assignment and unset
- name: "Variable set then unset errors on access"
ignore_stderr: true
stdin: |
set -u
var="value"
echo "$var"
unset var
echo "$var"
echo "should not print"
- name: "Variable set then unset with default works"
stdin: |
set -u
var="value"
unset var
echo "${var:-default}"
- name: "Unsetting non-existent variable does not error"
stdin: |
set -u
unset non_existent
echo "ok"
# Export and environment
- name: "Exported unset variable errors"
ignore_stderr: true
stdin: |
set -u
export unset_exported
echo "$unset_exported"
echo "should not print"
- name: "Exported variable with value works"
stdin: |
set -u
export exported_var="value"
echo "$exported_var"
# Pattern expansion
- name: "Unset variable in pattern errors"
ignore_stderr: true
stdin: |
set -u
echo ${unset_var#prefix}
echo "should not print"
# Length operator
- name: "Length of unset variable errors"
ignore_stderr: true
stdin: |
set -u
echo ${#unset_var}
echo "should not print"
- name: "Length of empty variable works"
stdin: |
set -u
empty=""
echo ${#empty}
# Indirect expansion
- name: "Indirect expansion of unset variable errors"
ignore_stderr: true
stdin: |
set -u
ref="unset_target"
echo ${!ref}
echo "should not print"
- name: "Indirect expansion to set variable works"
stdin: |
set -u
target="value"
ref="target"
echo ${!ref}
# Case statements
- name: "Unset variable in case pattern errors"
ignore_stderr: true
stdin: |
set -u
case "$unset_var" in
*) echo "matched" ;;
esac
echo "should not print"
- name: "Unset variable with default in case works"
stdin: |
set -u
case "${unset_var:-default}" in
default) echo "matched" ;;
esac
# For loops
- name: "Unset variable in for loop errors"
ignore_stderr: true
stdin: |
set -u
for item in $unset_var; do
echo "$item"
done
echo "should not print"
- name: "Unset variable with default in for loop works"
stdin: |
set -u
for item in ${unset_var:-a b c}; do
echo "$item"
done
# While loops with unset variables
- name: "Unset variable in while condition errors"
ignore_stderr: true
stdin: |
set -u
while [ "$unset_var" != "stop" ]; do
echo "loop"
break
done
echo "should not print"
# Read command
- name: "read into variable then access works"
stdin: |
set -u
echo "value" | read my_var || true
echo "${my_var:-default}"
- name: "Variable unset before read then set works"
stdin: |
set -u
echo "test" | {
read input
echo "$input"
}
# Compound assignments
- name: "Associative array unset key with default"
stdin: |
set -u
declare -A assoc
echo "${assoc[key]:-default}"
# Pipelines
- name: "Unset variable with default in pipeline works"
stdin: |
set -u
echo "${unset_var:-default}" | cat
# Here documents and here strings
- name: "Unset variable in here doc errors"
ignore_stderr: true
stdin: |
set -u
cat <<EOF
$unset_var
EOF
echo "should not print"
- name: "Unset variable with default in here doc works"
stdin: |
set -u
cat <<EOF
${unset_var:-default}
EOF
# Edge cases
- name: "Empty string is not unset"
stdin: |
set -u
var=""
echo "var is: '$var'"
- name: "Null value vs unset"
stdin: |
set -u
null=""
echo "${null-default}"
echo "${unset-default}"
- name: "IFS unset behavior"
stdin: |
set -u
unset IFS
echo "ok"
- name: "PATH unset would error"
ignore_stderr: true
stdin: |
set -u
saved_path="$PATH"
unset PATH
echo "$PATH"
echo "should not print"
# Combination with other options
- name: "set -u with set -e"
ignore_stderr: true
stdin: |
set -eu
echo "$unset_var"
echo "should not print"
# Declare without assignment
- name: "declare without value then access errors"
ignore_stderr: true
stdin: |
set -u
declare my_var
echo "$my_var"
echo "should not print"
- name: "declare with value then access works"
stdin: |
set -u
declare my_var="value"
echo "$my_var"
- name: "readonly without value then access errors"
ignore_stderr: true
stdin: |
set -u
readonly my_var
echo "$my_var"
echo "should not print"
- name: "Nameref to set variable with set -u"
known_failure: true
stdin: |
set -u
target="value"
declare -n ref=target
echo "$ref"
- name: "Nameref to unset variable with set -u errors"
known_failure: true
ignore_stderr: true
stdin: |
set -u
declare -n ref=unset_target
echo "$ref"
echo "should not print"
@@ -0,0 +1,81 @@
name: "set -x"
cases:
- name: "basic"
stdin: |
set -x
ls
for f in 1 2 3; do echo ${f}; done
case 1 in
1) echo "1";;
*) echo "not";;
esac
while false; do
echo body
done
newvar=$(echo "new")
x=$((3 + 7))
if [[ x == 10 && ! 0 ]]; then
echo "Math checks"
fi
var="x"
[[ ${var} && ${var//[[:space:]]/} ]]
for ((i = 0; i < 3; i++)); do
echo $i
done
((x = 3)) || ((x = 4))
override=value echo some_output
- name: "sourced script"
test_files:
- path: script.sh
contents: |
echo "Entered script"
for f in a b c; do echo $f; done
echo "Exiting script"
stdin: |
set -x
echo "About to source script"
source script.sh
echo "Back from script"
- name: "functions"
stdin: |
set -x
myfunc() {
echo "In myfunc"
for i in 1 2; do echo "i is $i"; done
}
echo "Before function call"
myfunc
echo "After function call"
- name: "command line"
args: ["-x", "script.sh"]
test_files:
- path: script.sh
contents: |
echo "In command line script"
for n in x y z; do echo $n; done
- name: "trap"
known_failure: true # TODO(traps): review this
stdin: |
set -x
trap 'echo "In trap"' DEBUG
echo "Doing something"
@@ -0,0 +1,79 @@
name: "Options: failglob"
cases:
- name: "failglob errors on unmatched glob"
ignore_stderr: true
stdin: |
shopt -s failglob
echo non-existent-*.txt 2>/dev/null
echo "exit=$?"
- name: "failglob does not affect matching glob"
stdin: |
touch realfile.txt
shopt -s failglob
echo real*.txt
- name: "failglob does not affect quoted glob chars"
stdin: |
shopt -s failglob
echo 'non-existent-*.txt'
- name: "failglob with negation"
ignore_stderr: true
stdin: |
shopt -s failglob
! echo non-existent-*.txt 2>/dev/null
echo "exit=$?"
- name: "failglob in subshell"
ignore_stderr: true
stdin: |
shopt -s failglob
(echo non-existent-*.txt; echo "inner=$?")
echo "outer=$?"
- name: "failglob lone ] is not a glob"
stdin: |
shopt -s failglob
echo foo]
- name: "failglob lone [ is not a glob"
stdin: |
shopt -s failglob
echo [abc
- name: "failglob aborts compound list"
ignore_stderr: true
stdin: |
shopt -s failglob
echo *.nomatch; echo "after"
- name: "failglob in multi-member pipeline"
ignore_stderr: true
stdin: |
shopt -s failglob
echo *.nomatch | cat
echo "after"
- name: "failglob extglob pattern"
ignore_stderr: true
stdin: |
shopt -s failglob
shopt -s extglob
echo @(nomatch_pattern_xyz)
echo "after"
- name: "failglob takes precedence over nullglob"
ignore_stderr: true
stdin: |
shopt -s failglob nullglob
echo non-existent-*.txt 2>/dev/null
echo "exit=$?"
- name: "failglob on unquoted variable with glob chars"
ignore_stderr: true
stdin: |
shopt -s failglob
x='*.nomatch_xyz'
echo $x 2>/dev/null
echo "exit=$?"
@@ -0,0 +1,11 @@
name: "shopt lastpipe"
cases:
- name: "shopt -s lastpipe"
stdin: |
echo ignored | read var
echo "1. var='${var}'"
shopt -s lastpipe
set +o monitor
echo ignored | read var
echo "2. var='${var}'"
@@ -0,0 +1,12 @@
name: "shopt login_shell"
cases:
- name: "login_shell is on for login shells"
args:
- "--login"
- "-c"
- "shopt -q login_shell"
- name: "login_shell is off for non-login shells"
args:
- "-c"
- "shopt -q login_shell"
@@ -0,0 +1,79 @@
name: "Options: nullglob"
cases:
- name: "nullglob removes unmatched glob"
stdin: |
shopt -s nullglob
echo "result:" non-existent-*.txt
- name: "nullglob does not affect matching glob"
stdin: |
touch realfile.txt
shopt -s nullglob
echo real*.txt
- name: "nullglob does not affect quoted glob chars"
stdin: |
shopt -s nullglob
echo 'non-existent-*.txt'
- name: "nullglob does not affect literal text without glob chars"
stdin: |
shopt -s nullglob
echo hello world
- name: "nullglob with multiple unmatched patterns"
stdin: |
shopt -s nullglob
echo "result:" *.zzz *.yyy *.xxx
- name: "nullglob with mix of matched and unmatched"
stdin: |
touch realfile.txt
shopt -s nullglob
echo real*.txt *.zzz
- name: "nullglob with question mark glob"
stdin: |
shopt -s nullglob
echo "result:" ?.nonexistent
- name: "nullglob with bracket glob"
stdin: |
shopt -s nullglob
echo "result:" [abc].nonexistent
- name: "nullglob disabled by default"
stdin: |
echo *.nonexistent
- name: "nullglob can be toggled"
stdin: |
shopt -s nullglob
echo "on:" *.nonexistent
shopt -u nullglob
echo "off:" *.nonexistent
- name: "nullglob in subshell"
stdin: |
shopt -s nullglob
(echo "inner:" *.nonexistent)
echo "outer:" *.nonexistent
- name: "nullglob with for loop"
stdin: |
shopt -s nullglob
for f in *.nonexistent; do
echo "unexpected: $f"
done
echo "done"
- name: "nullglob with array assignment"
stdin: |
shopt -s nullglob
arr=(*.nonexistent)
echo "count: ${#arr[@]}"
- name: "nullglob with directory glob"
stdin: |
shopt -s nullglob
echo "result:" nonexistent_dir/*/