name: "Variable tests" cases: - name: "Appending to a variable" stdin: | x=something x+=here echo "x: ${x}" - name: "Appending an integer to an integer variable" stdin: | declare -i x=10 echo "x: ${x}" x+=5 echo "x: ${x}" x+=value echo "x: ${x}" y=value declare -i y echo "y: ${y}" y+=5 echo "y: ${y}" - name: "Append to an unset variable" stdin: | declare -a myvar myvar+=abc echo "myvar: ${myvar}" declare -i myint myint+=abc echo "myint: ${myint}" - name: "Parameter length of unset variable with nounset" ignore_stderr: true stdin: | set -u echo "${#unset_var}" 2>&1 || true arr=() echo "${#arr[0]}" echo "${#arr[@]}" arr2=(hello world) echo "${#arr2[5]}" echo "${#arr2[0]}" - name: "local -I inherits from enclosing scope" stdin: | g=global-value f() { local -I g echo "[$g]" } f echo "[$g]" - name: "local -I with append" stdin: | v=base f() { local -I v+=" +more" echo "[$v]" } f echo "[$v]" - name: "local -I combined flags (-Ix)" stdin: | f() { local -Ix e declare -p e } f - name: "local -I without an enclosing variable falls through to normal creation" stdin: | f() { local -I nosuch="fresh" echo "[$nosuch]" } f - name: "local -I with a matching explicit array kind inherits value and attributes" stdin: | declare -ax a=(one two) declare -A m=([k]=v) f() { local -I -a a; declare -p a local -I -A m; declare -p m } f - name: "local -I when a same-name local already exists updates it in place" stdin: | v=outer f() { local v=first; local -I v+=suffix; declare -p v; } f declare -p v - name: "local without -I does not inherit the enclosing array kind" stdin: | declare -A m=([foo]=x) f() { local m[foo]=y; declare -p m; } f declare -p m # # `set -a` gives every variable a declaration creates or modifies the export attribute. # That includes a `local -I` that updates a same-name local already in the current scope. # - name: "local -I over a same-scope local honors set -a auto-export" stdin: | v=outer f() { local v=first; set -a; local -I v+=suffix; declare -p v; } f # # Dynamic variables (DIRSTACK and friends) resolve to real values when inherited. The element # count is compared to its own pre-inheritance value rather than pinned; the count itself is # covered separately (see "DIRSTACK includes the current directory"). # - name: "local -I -a over a dynamic variable inherits its resolved contents" stdin: | mkdir sub pushd sub > /dev/null before=${#DIRSTACK[@]} f() { local -I -a DIRSTACK; echo "inherited_all=$(( ${#DIRSTACK[@]} == before ))"; } f # # `local -g` declares into the global scope instead of the function's own, the way `declare -g` # does. brush's `local` always creates a local, so `-g` is silently ignored: the variable # disappears when the function returns, and a same-name local is written instead of the global. # - name: "local -g creates a global rather than a local" known_failure: true # TODO(local): `local -g` is ignored; the declaration is always local stdin: | f() { local -g g=1; } f declare -p g - name: "local -g updates the global while a same-name local shadows it" known_failure: true # TODO(local): `local -g` is ignored; the declaration is always local stdin: | h=outer f() { local h=inner local -g h=global echo "inside: $h" } f echo "outside: $h" - name: "local -g carries its attributes and array kind to the global" known_failure: true # TODO(local): `local -g` is ignored; the declaration is always local stdin: | f() { local -gx s=str local -g -a arr=(x y) } f declare -p s arr - name: "local -g with a bare name declares an unset global" known_failure: true # TODO(local): `local -g` is ignored; the declaration is always local stdin: | f() { local -g bare; } f declare -p bare echo "status=$?"