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,134 @@
name: "Array expansion"
cases:
- name: "Array access"
stdin: |
y=(a b c)
echo ${y[@]}
echo ${y[*]}
echo "\${y[@]}: ${y[@]}"
echo "\${y[*]}: ${y[*]}"
var1="${y[@]}"
echo "var1: ${var1}"
echo $var1
var2=${y[@]}
echo "var2: ${var2}"
echo $var2
var3="${y[*]}"
echo "var3: ${var3}"
echo $var3
var4=${y[*]}
echo "var4: ${var4}"
echo $var4
for f in "${y[@]}"; do
echo "quoted-@ => Element: $f"
done
for f in ${y[@]}; do
echo "@ => Element: $f"
done
for f in "${y[*]}"; do
echo "quoted-* => Element: $f"
done
for f in ${y[*]}; do
echo "* => Element: $f"
done
- name: "Empty arrays"
stdin: |
myarray=()
for f in "${myarray[@]}"; do
echo "Quoted at-sign element: |$f|"
done
for f in ${myarray[@]}; do
echo "Unquoted at-sign element: |$f|"
done
for f in "${myarray[*]}"; do
echo "Quoted at-sign element: |$f|"
done
for f in ${myarray[*]}; do
echo "Unquoted at-sign element: |$f|"
done
- name: "Mixing array elements and other words"
stdin: |
myarray=(a b c)
for f in "${myarray[@]} d e f"; do
echo "ELEMENT: $f"
done
for f in ${myarray[@]} d e f; do
echo "ELEMENT: $f"
done
- name: "Passing array elements to func"
stdin: |
myfunc() {
echo "In function"
for arg in $@; do
echo "ARG: |${arg}|"
done
}
arr=(a b c "" "e f")
myfunc ${arr[@]}
myfunc "${arr[@]}"
- name: "Array length"
stdin: |
y=(a b c)
echo "len(y) = ${#y}"
echo "len(y[*]) = ${#y[*]}"
echo "len(y[@]) = ${#y[@]}"
- name: "Array length: single item"
stdin: |
z=("something here")
echo "len(z) = ${#z}"
echo "len(z[*]) = ${#z[*]}"
echo "len(z[@]) = ${#z[@]}"
- name: "Array keys: indexed array"
stdin: |
arr=("element1" "element2" "element3")
echo "@: ${!arr[@]}"
echo "*: ${!arr[*]}"
echo "Dumping [@]"
for i in "${!arr[@]}"; do
echo "@i: $i"
done
echo "Dumping [*]"
for i in "${!arr[*]}"; do
echo "*i: $i"
done
- name: "Array keys: empty array"
stdin: |
arr=()
echo "@: ${!arr[@]}"
echo "*: ${!arr[*]}"
echo "Dumping [@]"
for i in "${!arr[@]}"; do
echo "@i: $i"
done
echo "Dumping [*]"
for i in "${!arr[*]}"; do
echo "*i: $i"
done
@@ -0,0 +1,145 @@
name: "Brace expansion"
cases:
- name: "Expansion with curly braces: commas"
stdin: |
echo "{a,b} =>"
echo {a,b}
echo
echo "{a} =>"
echo {a}
echo
echo "{a} =>"
echo {a}
echo
echo "a{,}b =>"
echo a{,}b
echo
echo "{} =>"
echo {}
echo
echo "1{a,b}2 =>"
echo 1{a,b}2
echo
echo "1{a,b}2{c,d}3 =>"
echo 1{a,b}2{c,d}3
echo
echo "{a,b}{1,2} =>"
echo {a,b}{1,2}
echo
echo 'W\{1,2}X =>'
echo W\{1,2}X
echo
echo 'W{1,2\}X =>'
echo W{1,2\}X
echo
echo '"W{1,2}X" =>'
echo "W{1,2}X"
echo
var=value
bar=other
echo '{$var,$ba}r =>'
echo {$va,$ba}r
echo
echo '\${a,b} =>'
echo \${a,b}
echo
- name: "Expansion with curly braces: alpha ranges"
stdin: |
echo "{a..f} =>"
echo {a..f}
echo
echo "{a..f..2} =>"
echo {a..f..2}
echo
echo "{a..f..0} =>"
echo {a..f..0}
echo
echo "{m..f} =>"
echo {m..f}
echo
- name: "Expansion with curly braces: numeric ranges"
stdin: |
echo "{2..9} =>"
echo {2..9}
echo
echo "{+2..+9} =>"
echo {+2..+9}
echo
echo "{2..2} =>"
echo {2..2}
echo
echo "{2..1} =>"
echo {2..1}
echo
echo "{10..2..0} =>"
echo {10..2..0}
echo
echo "{10..2..2} =>"
echo {10..2..2}
echo
echo "{10..2..-2} =>"
echo {10..2..-2}
echo
echo "{2..9..2} =>"
echo {2..9..2}
echo
echo "{2..9..0} =>"
echo {2..9..0}
echo
echo "{-1..-10} =>"
echo {-1..-10}
echo
echo "{-10..-1} =>"
echo {-10..-1}
echo
echo "{-1..-10..2} =>"
echo {-1..-10..2}
echo
- name: "Expansion with curly braces: nested"
stdin: |
echo "W{{1..3},a,x}Y =>"
echo W{{1..3},a,x}Y
echo
- name: "Iterate through modified array"
stdin: |
array=("aa" "ba" "ca")
echo "With quotes:"
for i in "${array[@]%a}"; do
echo "Element: '$i'"
done
echo "Without quotes:"
for i in ${array[@]%a}; do
echo "Element: '$i'"
done
@@ -0,0 +1,222 @@
name: "Command substitution"
cases:
- name: "Command substitution"
stdin: |
var="value"
echo "1:"
echo $(echo hi)
echo "2:"
echo "$(echo hi)"
echo "3:"
echo "$(echo "hi")"
echo "4:"
echo "$(var="updated"; echo ${var})"
echo "var=${var}"
- name: "Command substitution with embedded parens"
stdin: |
x=$(echo foo | (wc -l; echo hi))
echo "\$x: $x"
- name: "Command substitution with subshell"
stdin: |
x=$( (echo hi) )
echo "\$x: $x"
- name: "Command substitution exit code"
stdin: |
x=$(false) && echo "1. Made it past false"
y=$(true) && echo "2. Made it past true"
- name: "Command substitution with exec"
stdin: |
x=$(exec echo hi)
echo "x: $x"
- name: "Backtick command substitution"
stdin: |
echo `echo hi`
- name: "Backtick command substitution with escaping"
stdin: |
echo `echo \`echo hi\``
- name: "Backtick command substitution with backslashes"
stdin: |
echo `echo \\`
- name: "Backtick command substitution in double quotes"
stdin: |
echo "`echo First line`"
echo " `echo Second line` "
echo "Third`echo line`here"
- name: "Ignore single quote in comment in command substitution"
stdin: |
var=$(
# I'm
echo "Batman"
)
echo $var
- name: "Ignore double quote in comment in command substitution"
stdin: |
var=$(
# This " is not being
echo "parsed"
)
echo $var
- name: "Ignore parentheses in comment in command substitution"
stdin: |
var=$(
# :(
echo "Sad"
)
echo $var
- name: "Ignore dollar in comment in command substitution"
stdin: |
var=$(
# $
echo "Mr. Crabs ^"
)
echo $var
- name: "Positional parameter count not mistaken for comment"
stdin: |
echo $(echo $#)
- name: "Ignore single quote in comment in command substitution (backticks)"
stdin: |
var=`
# I'm
echo "Batman"
`
echo $var
- name: "Ignore double quote in comment in command substitution (backticks)"
stdin: |
var=`
# This " is not being
echo "parsed"
`
echo $var
- name: "Ignore parentheses in comment in command substitution (backticks)"
stdin: |
var=`
# :(
echo "Sad"
`
echo $var
- name: "Ignore dollar in comment in command substitution (backticks)"
stdin: |
var=`
# $
echo "Mr. Crabs ^"
`
echo $var
- name: "Positional parameter count not mistaken for comment (backticks)"
stdin: |
echo `echo $#`
- name: "Command substitution with only input redir"
stdin: |
echo "Some text" >file.txt
x=$(<file.txt)
echo "x: $x"
- name: "Command substitution with only input redir in pipeline"
known_failure: true
ignore_stderr: true
test_files:
- path: file.txt
contents: |
File Contents
stdin: |
x=$(<non-existent-file.txt) || x=$(<file.txt)
echo "\$?: $?"
echo "x: $x"
- name: "Backquote with only input redir"
stdin: |
echo "Some text" >file.txt
x=`<file.txt`
echo "x: $x"
- name: "Command substitution as simple command preserves exit code"
stdin: |
$(false)
echo "exit code: $?"
$(true)
echo "exit code: $?"
$(exit 42)
echo "exit code: $?"
- name: "Backtick substitution as simple command preserves exit code"
stdin: |
`false`
echo "exit code: $?"
`true`
echo "exit code: $?"
- name: "Command substitution with exec as simple command"
stdin: |
$(exec true)
echo "exit code: $?"
$(exec false)
echo "exit code: $?"
- name: "Command substitution strips null bytes"
ignore_stderr: true
stdin: |
echo "$(echo -e 'a\x00b')" | hexdump -C
- name: "Backtick substitution strips null bytes"
ignore_stderr: true
stdin: |
echo "`echo -e 'a\x00b'`" | hexdump -C
- name: "Command substitution strips multiple null bytes"
ignore_stderr: true
stdin: |
echo "$(echo -e 'hello\x00world\x00end')" | hexdump -C
- name: "Command substitution with only null bytes produces empty string"
ignore_stderr: true
stdin: |
echo "$(echo -e '\x00\x00\x00')" | hexdump -C
- name: "Variable assignment from command substitution strips null bytes"
ignore_stderr: true
stdin: |
x=$(echo -e 'a\x00b')
echo "$x" | hexdump -C
- name: "Input redirection substitution strips null bytes"
ignore_stderr: true
stdin: |
printf 'a\x00b' > file.txt
x=$(<file.txt)
echo "$x" | hexdump -C
- name: "Nested command substitution strips null bytes"
ignore_stderr: true
stdin: |
echo "$(echo "$(echo -e 'a\x00b')")" | hexdump -C
- name: "Nested command substitution exit code"
stdin: |
$($(false))
echo "exit code: $?"
$($(:))
echo "exit code: $?"
@@ -0,0 +1,139 @@
name: "Parameter transformation"
cases:
- name: "Parameter case transformation"
stdin: |
var="hElLo WoRlD"
echo "${var@U}"
echo "${var@u}"
echo "${var@L}"
var=("hElLo WoRlD")
echo "${var@U}"
echo "${var@u}"
echo "${var@L}"
echo "${var[*]@U}"
echo "${var[*]@u}"
echo "${var[*]@L}"
echo "${var[@]@U}"
echo "${var[@]@u}"
echo "${var[@]@L}"
- name: "Parameter quote transformations - Q"
stdin: |
var='""'
echo "\${var@Q}: ${var@Q}"
var="Hello"
echo "\${var@Q}: ${var@Q}"
var="Hello, world!"
echo "\${var@Q}: ${var@Q}"
var="a 'b c' d"
echo "\${var@Q}: ${var@Q}"
declare -a arr1=(a b c)
echo "\${arr1@Q}: ${arr1@Q}"
declare -A arr2=(["a"]=1 ["b"]=2)
echo "\${arr2@Q}: ${arr2@Q}"
- name: "Parameter quote transformations - K"
known_failure: true # TODO(expansion): needs triage and debugging
stdin: |
var='""'
echo "\${var@K}: ${var@K}"
var="Hello"
echo "\${var@K}: ${var@K}"
var="Hello, world!"
echo "\${var@K}: ${var@K}"
var="a 'b c' d"
echo "\${var@K}: ${var@K}"
declare -a arr1=(a b c)
echo "\${arr1@K}: ${arr1@K}"
echo "\${arr1[1]@K}: ${arr1[1]@K}"
echo "\${arr1[@]@K}: ${arr1[@]@K}"
echo "\${arr1[*]@K}: ${arr1[*]@K}"
declare -A arr2=(["a"]=1 ["b"]=2)
echo "\${arr2@K}: ${arr2@K}"
echo "\${arr2[b]@K}: ${arr2[b]@K}"
echo "\${arr2[@]@K}: ${arr2[@]@K}"
echo "\${arr2[*]@K}: ${arr2[*]@K}"
- name: "Parameter quote transformations - k"
min_oracle_version: 5.2
stdin: |
var='""'
echo "\${var@k}: ${var@k}"
var="Hello"
echo "\${var@k}: ${var@k}"
var="Hello, world!"
echo "\${var@k}: ${var@k}"
declare -a arr1=(a b c)
echo "\${arr1@k}: ${arr1@k}"
declare -A arr2=(["a"]=1 ["b"]=2)
echo "\${arr2@k}: ${arr2@k}"
- name: "Parameter transformations: expand escapes"
stdin: |
var="a\n\"b"
echo "\${var@E}: ${var@E}"
- name: "Parameter transformation: assignment"
stdin: |
var="hello"
echo "\${var@A}: ${var@A}"
declare -ia arr=(1 2 3)
echo "\${arr@A}: ${arr@A}"
echo "\${arr[1]@A}: ${arr[1]@A}"
echo "\${arr[@]@A}: ${arr[@]@A}"
echo "\${arr[*]@A}: ${arr[*]@A}"
declare -A arr2=(["a"]=b)
echo "\${arr2@A}: ${arr2@A}"
echo "\${arr2[@]@A}: ${arr2[@]@A}"
echo "\${arr2[*]@A}: ${arr2[*]@A}"
- name: "Parameter transformation: attributes"
stdin: |
var="hello"
echo "\${var@a}: ${var@a}"
declare -ia arr=(1 2 3)
echo "\${arr@a}: ${arr@a}"
ref="arr"
echo "\${!ref@a}: ${!ref@a}"
- name: "Parameter transformation @a through nameref"
known_failure: true
stdin: |
declare -i target=42
declare -n ref=target
echo "ref @a: ${ref@a}"
echo "target @a: ${target@a}"
- name: "Parameter transformation @Q through nameref"
known_failure: true
stdin: |
target="hello world"
declare -n ref=target
echo "ref @Q: ${ref@Q}"
- name: "Parameter transformation @A through nameref"
known_failure: true
stdin: |
target="hello"
declare -n ref=target
echo "ref @A: ${ref@A}"
echo "target @A: ${target@A}"
@@ -0,0 +1,575 @@
name: "Parameter expansion"
cases:
- name: "String length"
stdin: |
x="abc"
echo "len = ${#x}"
- name: "Parameter expression: default value"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# -
echo "\${value-default} : ${value-default}"
echo "\${empty-default} : ${empty-default}"
echo "\${declared-default} : ${declared-default}"
echo "\${undeclared-default} : ${undeclared-default}"
echo "\${value-} : ${value-}"
echo "\${empty-} : ${empty-}"
echo "\${declared-} : ${declared-}"
echo "\${undeclared-} : ${undeclared-}"
# :-
echo "\${value:-default} : ${value:-default}"
echo "\${empty:-default} : ${empty:-default}"
echo "\${declared:-default} : ${declared:-default}"
echo "\${undeclared:-default} : ${undeclared:-default}"
echo "\${value:-} : ${value:-}"
echo "\${empty:-} : ${empty:-}"
echo "\${declared:-} : ${declared:-}"
echo "\${undeclared:-} : ${undeclared:-}"
- name: "Parameter expression: assign default value (no colon)"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# =
echo "\${value=default} : ${value=default}"
declare -p value
echo "\${empty=default} : ${empty=default}"
declare -p empty
echo "\${declared=default} : ${declared=default}"
declare -p declared
echo "\${undeclared=default} : ${undeclared=default}"
declare -p undeclared
- name: "Parameter expression: assign default value (no colon, empty replacement)"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# =
echo "\${value=} : ${value=}"
declare -p value
echo "\${empty=} : ${empty=}"
declare -p empty
echo "\${declared=} : ${declared=}"
declare -p declared
echo "\${undeclared=} : ${undeclared=}"
declare -p undeclared
- name: "Parameter expression: assign default value (colon)"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# :=
echo "\${value:=default} : ${value:=default}"
declare -p value
echo "\${empty:=default} : ${empty:=default}"
declare -p empty
echo "\${declared:=default} : ${declared:=default}"
declare -p declared
echo "\${undeclared:=default} : ${undeclared:=default}"
declare -p undeclared
- name: "Parameter expression: assign default value (colon, empty replacement)"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# :=
echo "\${value:=} : ${value:=}"
declare -p value
echo "\${empty:=} : ${empty:=}"
declare -p empty
echo "\${declared:=} : ${declared:=}"
declare -p declared
echo "\${undeclared:=} : ${undeclared:=}"
declare -p undeclared
- name: "Parameter expression: alternative value"
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# +
echo "\${value+default} : ${value+default}"
echo "\${empty+default} : ${empty+default}"
echo "\${declared+default} : ${declared+default}"
echo "\${undeclared+default} : ${undeclared+default}"
echo "\${value+} : ${value+}"
echo "\${empty+} : ${empty+}"
echo "\${declared+} : ${declared+}"
echo "\${undeclared+} : ${undeclared+}"
# :+
echo "\${value:+default} : ${value:+default}"
echo "\${empty:+default} : ${empty:+default}"
echo "\${declared:+default} : ${declared:+default}"
echo "\${undeclared:+default} : ${undeclared:+default}"
echo "\${value:+} : ${value:+}"
echo "\${empty:+} : ${empty:+}"
echo "\${declared:+} : ${declared:+}"
echo "\${undeclared:+} : ${undeclared:+}"
- name: "Parameter expression: advanced alternative value"
stdin: |
value="value"
echo ${value:+\x}
echo ${value:+'x'}
echo "${value:+\x}"
echo "${value:+'x'}"
echo "${value:+x y}"
echo "${value:+z 'x' 'y' w}"
echo "${value:+x'y'x}"
echo "${value:+"x y"}"
echo "${value:+"x\xy"}"
echo "${value:+z"x\xy"z}"
- name: "Parameter expression: error on condition (interactive)"
ignore_stderr: true
args: ["-i"]
stdin: |
value="value"
empty=""
declare declared
unset undeclared
# ?
echo "\${value?error message} : ${value?error message}"
echo " -> result: $?"
echo "\${empty?error message} : ${empty?error message}"
echo " -> result: $?"
echo "\${declared?error message} : ${declared?error message}"
echo " -> result: $?"
echo "\${undeclared?error message} : ${undeclared?error message}"
echo " -> result: $?"
echo "\${value?} : ${value?}"
echo " -> result: $?"
echo "\${empty?} : ${empty?}"
echo " -> result: $?"
echo "\${declared?} : ${declared?}"
echo " -> result: $?"
echo "\${undeclared?} : ${undeclared?}"
echo " -> result: $?"
# :?
echo "\${value:?error message} : ${value:?error message}"
echo " -> result: $?"
echo "\${empty:?error message} : ${empty:?error message}"
echo " -> result: $?"
echo "\${declared:?error message} : ${declared:?error message}"
echo " -> result: $?"
echo "\${undeclared:?error message} : ${undeclared:?error message}"
echo " -> result: $?"
echo "\${value:?} : ${value:?}"
echo " -> result: $?"
echo "\${empty:?} : ${empty:?}"
echo " -> result: $?"
echo "\${declared:?} : ${declared:?}"
echo " -> result: $?"
echo "\${undeclared:?} : ${undeclared:?}"
echo " -> result: $?"
- name: "Parameter expression: error on condition (non-interactive)"
ignore_stderr: true
stdin: |
echo "${non_existent_var?error message}"
echo "This should never execute"
- name: "Parameter expression: expanded array as alternate value"
stdin: |
declare -a var=("abc" "def" "ghi" "")
for item in "${var[@]}"; do
echo "Item: '${item}'"
done
echo "Expression 1: '${var+"${var[@]}"}'"
for item in "${var+"${var[@]}"}"; do
echo " -> '${item}'"
done
echo "Expression 2: '${var+${var[@]}}'"
for item in "${var+${var[@]}}"; do
echo " -> '${item}'"
done
echo "Expression 3: '${var+"${var[@]}"}'"
for item in ${var+"${var[@]}"}; do
echo " -> '${item}'"
done
echo "Expression 4: '${var+${var[@]}}'"
for item in ${var+${var[@]}}; do
echo " -> '${item}'"
done
echo "Expression 5: '${var+"${var[*]}"}'"
for item in "${var+"${var[*]}"}"; do
echo " -> '${item}'"
done
echo "Expression 6: '${var+${var[*]}}'"
for item in "${var+${var[*]}}"; do
echo " -> '${item}'"
done
echo "Expression 7: '${var+"${var[*]}"}'"
for item in ${var+"${var[*]}"}; do
echo " -> '${item}'"
done
echo "Expression 8: '${var+${var[*]}}'"
for item in ${var+${var[*]}}; do
echo " -> '${item}'"
done
- name: "Parameter expression: expanded array as default value"
stdin: |
declare -a var=("abc" "def" "ghi" "")
for item in "${var[@]}"; do
echo "Item: '${item}'"
done
echo "Expression 1: ${nonexistent-${var[@]}}"
for item in ${nonexistent-${var[@]}}; do
echo " -> '${item}'"
done
echo "Expression 2: ${nonexistent-"${var[@]}"}"
for item in ${nonexistent-"${var[@]}"}; do
echo " -> '${item}'"
done
echo "Expression 3: ${nonexistent2=${var[@]}}"
for item in ${nonexistent3=${var[@]}}; do
echo " -> '${item}'"
done
echo "Expression 4: ${nonexistent4="${var[@]}"}"
for item in ${nonexistent5-"${var[@]}"}; do
echo " -> '${item}'"
done
- name: "Remove prefix/suffix"
stdin: |
var="prepre-abc-sufsuf"
# Smallest suffix
shopt -u nocasematch
echo "\${var%}: ${var%}"
echo "\${var%pre}: ${var%pre}"
echo "\${var%suf}: ${var%suf}"
echo "\${var%SUF}: ${var%SUF}"
shopt -s nocasematch
echo "\${var%SUF}(nocasematch): ${var%SUF}"
# Largest suffix
shopt -u nocasematch
echo "\${var%%}: ${var%%}"
echo "\${var%%pre}: ${var%%pre}"
echo "\${var%%suf}: ${var%%suf}"
echo "\${var%%SUF}: ${var%%SUF}"
shopt -s nocasematch
echo "\${var%%SUF}(nocasematch): ${var%%SUF}"
# Smallest prefix
shopt -u nocasematch
echo "\${var#}: ${var#}"
echo "\${var#pre}: ${var#pre}"
echo "\${var#suf}: ${var#suf}"
echo "\${var#PRE}: ${var#PRE}"
shopt -s nocasematch
echo "\${var#PRE}(nocasematch): ${var#PRE}"
# Largest prefix
shopt -u nocasematch
echo "\${var##}: ${var##}"
echo "\${var##pre}: ${var##pre}"
echo "\${var##suf}: ${var##suf}"
echo "\${var##PRE}: ${var##PRE}"
shopt -s nocasematch
echo "\${var##PRE}(nocasematch): ${var##PRE}"
- name: "Indirect variable references"
stdin: |
var="Hello"
ref="var"
echo "${!ref}"
echo "${!ref//l/o}"
- name: "Indirect variable references with special parameters"
stdin: |
set a b c
ref="2"
echo "${!ref}"
- name: "Indirect variable references with array"
stdin: |
arr=("element1" "element2" "element3")
ref="arr[1]"
echo "${!ref}"
ref="arr[10]"
echo "${!ref}"
- name: "Variable prefix match"
stdin: |
var1="Hello"
var2="World"
echo "${!var*}"
echo "${!var@}"
echo "Dumping *"
for i in "${!var*}"; do
echo "i: $i"
done
echo "Dumping @"
for i in "${!var@}"; do
echo "i: $i"
done
- name: "Uppercase first character"
stdin: |
var="hello"
shopt -u nocasematch
echo "\${var^}: ${var^}"
echo "\${var^h}: ${var^h}"
echo "\${var^H}: ${var^H}"
echo "\${var^l}: ${var^l}"
echo "\${var^h*}: ${var^h*}"
echo "\${var^he}: ${var^he}"
echo "\${var^?}: ${var^?}"
echo "\${var^*}: ${var^*}"
shopt -s nocasematch
echo "\${var^H}(nocasematch): ${var^H}"
arr=("hello" "world")
echo "\${arr^}: ${arr^}"
echo "\${arr[@]^}: ${arr[@]^}"
echo "\${arr[*]^}: ${arr[*]^}"
- name: "Uppercase matching pattern"
stdin: |
var="hello"
shopt -u nocasematch
echo "\${var^^}: ${var^^}"
echo "\${var^^l}: ${var^^l}"
echo "\${var^^L}: ${var^^L}"
echo "\${var^^m}: ${var^^m}"
shopt -s nocasematch
echo "\${var^^L}(nocasematch): ${var^^L}"
arr=("hello" "world")
echo "\${arr^^}: ${arr^^}"
echo "\${arr[@]^^}: ${arr[@]^^}"
echo "\${arr[*]^^}: ${arr[*]^^}"
- name: "Lowercase first character"
stdin: |
var="HELLO"
shopt -u nocasematch
echo "\${var,}: ${var,}"
echo "\${var,h}: ${var,h}"
echo "\${var,H}: ${var,H}"
echo "\${var,L}: ${var,L}"
echo "\${var,H*}: ${var,H*}"
echo "\${var,HE}: ${var,HE}"
echo "\${var,?}: ${var,?}"
echo "\${var,*}: ${var,*}"
shopt -s nocasematch
echo "\${var,h}(nocasematch): ${var,h}"
arr=("HELLO" "WORLD")
echo "\${arr,}: ${arr,}"
echo "\${arr[@],}: ${arr[@],}"
echo "\${arr[*],}: ${arr[*],}"
- name: "Lowercase matching pattern"
stdin: |
var="HELLO"
shopt -u nocasematch
echo "\${var,,}: ${var,,}"
echo "\${var,,M}: ${var,,M}"
echo "\${var,,L}: ${var,,L}"
echo "\${var,,l}: ${var,,l}"
shopt -s nocasematch
echo "\${var,,l}(nocasematch): ${var,,l}"
arr=("HELLO" "WORLD")
echo "\${arr,,}: ${arr,,}"
echo "\${arr[@],,}: ${arr[@],,}"
echo "\${arr[*],,}: ${arr[*],,}"
- name: "Substring replacement"
stdin: |
var="Hello, world!"
echo "\${var/world/WORLD}: ${var/world/WORLD}"
arr=("world" "world")
echo "\${arr/world/WORLD}: ${arr/world/WORLD}"
echo "\${arr[@]/world/WORLD}: ${arr[@]/world/WORLD}"
echo "\${arr[*]/world/WORLD}: ${arr[*]/world/WORLD}"
- name: "Substring replacement with slashes"
stdin: |
var="Hello, world!"
echo "\${var/world//world}: ${var/world//world}"
- name: "Substring replacement with patterns"
stdin: |
var="a[bc]d"
pattern="[bc]"
echo "Replacement 1: ${var/${pattern}/}"
echo "Replacement 2: ${var/"${pattern}"/}"
- name: "Prefix substring replacement"
stdin: |
var="Hello, world!"
echo "\${var/#world/WORLD}: ${var/#world/WORLD}"
echo "\${var/#Hello/HELLO}: ${var/#Hello/HELLO}"
arr=("world" "world")
echo "\${arr/#world/WORLD}: ${arr/#world/WORLD}"
echo "\${arr[@]/#world/WORLD}: ${arr[@]/#world/WORLD}"
echo "\${arr[*]/#world/WORLD}: ${arr[*]/#world/WORLD}"
- name: "Suffix substring replacement"
stdin: |
var="Hello, world!"
echo "\${var/%Hello/HELLO}: ${var/%Hello/HELLO}"
echo "\${var/%world!/WORLD!}: ${var/%world!/WORLD!}"
arr=("world" "world")
echo "\${arr/%world/WORLD}: ${arr/%world/WORLD}"
echo "\${arr[@]/%world/WORLD}: ${arr[@]/%world/WORLD}"
echo "\${arr[*]/%world/WORLD}: ${arr[*]/%world/WORLD}"
- name: "Global substring replacement"
stdin: |
var="Hello, world, world!"
echo "\${var//world/WORLD}: ${var//world/WORLD}"
arr=("world world" "world world")
echo "\${arr//world/WORLD}: ${arr//world/WORLD}"
echo "\${arr[@]//world/WORLD}: ${arr[@]//world/WORLD}"
echo "\${arr[*]//world/WORLD}: ${arr[*]//world/WORLD}"
- name: "Global substring removal"
stdin: |
var="That is not all"
shopt -u nocasematch
echo "\${var//not }: ${var//not}"
shopt -s nocasematch
echo "\${var//NOT }: ${var//NOT }"
- name: "Substring from offset"
stdin: |
var="Hello, world!"
echo "\${var:0}: ${var:0}"
echo "\${var:7}: ${var:7}"
echo "\${var: 7 }: ${var: 7 }"
echo "\${var:50}: ${var:50}"
echo "\${var:-1}: ${var:-1}"
echo "\${var: -1}: ${var: -1}"
echo "\${var: 2 + 3 }: ${var: 2 + 3 }"
- name: "Substring with length"
stdin: |
var="Hello, world!"
echo "\${var:0:1}: ${var:0:1}"
echo "\${var:0:0}: ${var:0:0}"
echo "\${var:0:50}: ${var:0:50}"
echo "\${var:0:-1}: ${var:0:-1}"
echo "\${var:0:-3}: ${var:0:-3}"
echo "\${var:7:3}: ${var:7:3}"
echo "\${var:50:2}: ${var:50:2}"
echo "\${var:-1:1}: ${var:-1:1}"
echo "\${var:-3:1}: ${var:-3:1}"
- name: "Substring on string with multi-byte chars"
skip: true # TODO(expansion): succeeds on macOS, fails on Linux
stdin: |
var="7❌🖌️✅😂🔴⛔ABC"
echo "${var:2}" | hexdump -C
- name: "Substring operator on arrays"
stdin: |
set abcde fghij klmno pqrst uvwxy z
echo "\${@:2:2}: ${@:2:2}"
echo "\${@:2}: ${@:2}"
myarray=(abcde fghij klmno pqrst uvwxy z)
echo "\${myarray[@]:2:2}: ${myarray[@]:2:2}"
echo "\${myarray[@]:2}: ${myarray[@]:2}"
- name: "Substring operator on arrays with negative index"
stdin: |
set abcde fghij klmno pqrst uvwxy z
echo "\${@: -2:2}: ${@: -2:2}"
echo "\${@: -2}: ${@: -2}"
myarray=(abcde fghij klmno pqrst uvwxy z)
echo "\${myarray[@]: -2:2}: ${myarray[@]: -2:2}"
echo "\${myarray[@]: -2}: ${myarray[@]: -2}"
- name: "Substring operator on single-element array"
stdin: |
myarray=("abcde fghij klmno pqrst uvwxy z")
echo "\${myarray[@]:0:1}: ${myarray[@]:0:1}"
echo "\${myarray[@]:1}: ${myarray[@]:1}"
- name: "Substring operator past end of array"
stdin: |
set a b c
declare -a result1=("${@:3}")
declare -p result1
myarray=(a b c)
declare -a result2=("${myarray[@]:3}")
declare -p result2
- name: "Substring with length (with nested expressions)"
stdin: |
var="Hello, world!"
offset=7
length=5
echo "\${var:\$offset:\${length}}: ${var:$offset:${length}}"
@@ -0,0 +1,84 @@
name: "Quotes"
cases:
- name: "Single quotes"
stdin: |
var=xyz
echo 'Quoted string'
echo 'abc ${var} def'
- name: "Double quotes"
stdin: |
echo "\""
- name: "Double quotes: escaping"
stdin: |
echo "1. \'" | hexdump -C
echo "2. \x" | hexdump -C
echo "\n"
- name: "ANSI-C quotes"
stdin: |
single_quoted='\n'
echo "Single quoted len: ${#single_quoted}"
echo -n '\n' | hexdump -C
ansi_c_quoted=$'\n'
echo "ANSI-C quoted len: ${#ansi_c_quoted}"
echo -n $'\n' | hexdump -C
- name: "ANSI-C quotes with escape sequences"
stdin: |
echo -n "0. "$'\x' | hexdump -C
echo -n "1. "$'\x65' | hexdump -C
echo -n "2. "$'\xgg' | hexdump -C
echo -n "3. "$'\x{65}' | hexdump -C
echo -n "4. "$'\x{65' | hexdump -C
echo -n "5. "$'\x{65R}' | hexdump -C
echo -n "6. "$'\x{}R' | hexdump -C
echo -n "7. "$'\x{ }' | hexdump -C
echo -n "8. "$'\x{965}' | hexdump -C
echo -n "9. "$'\'\"' | hexdump -C
echo -n "10. "$'🐢' | hexdump -C
echo -n "11. "$'\101' | hexdump -C
echo -n "12. "$'\10' | hexdump -C
echo -n "13. "$'\1' | hexdump -C
echo -n "14. "$'\0' | hexdump -C
echo -n "15. "$'\001' | hexdump -C
echo -n "16. "$'abc \0 def' | hexdump -C
echo -n "17. "$'\ca' | hexdump -C
echo -n "18. "$'\cz' | hexdump -C
echo -n "19. "$'\cA' | hexdump -C
echo -n "20. "$'\cZ' | hexdump -C
echo -n "21. "$'\c ' | hexdump -C
echo -n "22. "$'\c!' | hexdump -C
echo -n "23. "$'\c/' | hexdump -C
echo -n "24. "$'\c0' | hexdump -C
echo -n "25. "$'\c9' | hexdump -C
echo -n "26. "$'\c:' | hexdump -C
echo -n "27. "$'\c@' | hexdump -C
echo -n "28. "$'\c[' | hexdump -C
echo -n "29. "$'\c]' | hexdump -C
echo -n "30. "$'\c`' | hexdump -C
echo -n "31. "$'\c{' | hexdump -C
echo -n "32. "$'\c?' | hexdump -C
echo -n "33. "$'\c*' | hexdump -C
echo -n "34. "$'\c^' | hexdump -C
echo -n "35. "$'\c_' | hexdump -C
echo -n "36. "$'\c' | hexdump -C
echo -n "37. "$'\c\\' | hexdump -C
echo -n "38. "$'\c\n' | hexdump -C
echo -n "39. "$'\\' | hexdump -C
- name: "ANSI-C quote syntax inside double quotes (literal, not processed)"
stdin: |
# When $'...' appears inside double quotes, it should be treated as literal text
echo "$'\t'"
echo "$'\'abcd\''"
x="$'\n'"
echo "len: ${#x}"
echo "$x"
- name: "gettext style quotes"
stdin: |
quoted=$"Hello, world"
echo "Content: [${quoted}]"
@@ -0,0 +1,141 @@
name: "Tilde expansion"
cases:
- name: "Tilde expansion: basic"
home_dir: .
stdin: |
test_root=$(realpath $(pwd))
mkdir subdir
cd subdir
D=~
D=$(realpath $D)
echo "~: ${D//${test_root}/TEST_ROOT}"
- name: "Tilde expansion: explicit user"
home_dir: .
stdin: |
test_root=$(pwd)
D=~root
echo "~root: $D"
D=~nonexistentuserwhodoesnotexist
echo "~<non-existent-username>: $D"
- name: "Tilde expansion: working dirs"
home_dir: .
stdin: |
test_root=$(pwd)
mkdir subdir
cd subdir
D=~+
echo "~+: ${D//${test_root}/TEST_ROOT}"
D=~-
echo "~-: ${D//${test_root}/TEST_ROOT}"
unset OLDPWD
D=~-
echo "~- (without OLDPWD): ${D//${test_root}/TEST_ROOT}"
- name: "Tilde expansion: dir stack"
stdin: |
test_root=$(pwd)
for name in a b c d e f g; do
mkdir $name
pushd $name >/dev/null 2>&1
done
D=~2
echo "~2: ${D//${test_root}/TEST_ROOT}"
D=~9
echo "~9: ${D//${test_root}/TEST_ROOT}"
D=~+2
echo "~+2: ${D//${test_root}/TEST_ROOT}"
D=~+9
echo "~+9: ${D//${test_root}/TEST_ROOT}"
D=~-2
echo "~-2: ${D//${test_root}/TEST_ROOT}"
D=~-9
echo "~-9: ${D//${test_root}/TEST_ROOT}"
- name: "Tilde expansion: after colons"
home_dir: .
stdin: |
test_root=~
D=something:~
echo "D: ${D//${test_root}/TEST_ROOT}"
D=something":"~
echo "D: ${D//${test_root}/TEST_ROOT}"
D=something:"~"
echo "D: ${D//${test_root}/TEST_ROOT}"
D=something:~:another:~
echo "D: ${D//${test_root}/TEST_ROOT}"
- name: "Tilde expansion: unexpanded tildes"
home_dir: .
stdin: |
test_root=$(pwd)
D="~"
echo "D: ${D//${test_root}/TEST_ROOT}"
D=x~
echo "D: ${D//${test_root}/TEST_ROOT}"
- name: "Tilde expansion: colon context sensitivity"
env:
HOME: /testhome
stdin: |
# Tilde at word start always expands (in both contexts)
echo "echo tilde: $(echo ~)"
var=~
echo "assignment tilde: ${var}"
# Tilde after colon: should NOT expand in non-assignment context
echo "echo with colon: $(echo ~:~/bin)"
echo ~:~/bin
# But SHOULD expand in assignment context
var=~:~/bin
echo "assignment with colon: ${var}"
# Complex PATH-like example
PATH=/usr/bin:~:/bin:~/local
echo "PATH: ${PATH}"
- name: "Tilde expansion: parameter defaults"
env:
HOME: /testhome
stdin: |
echo "Use default value with tilde"
unset myvar
echo "Default: ${myvar:-~}"
echo "myvar after :-: ${myvar}"
echo "Assign default value with tilde"
unset myvar
echo "Assign default: ${myvar:=~}"
echo "myvar after :=: ${myvar}"
echo "Use default with colon-tilde"
unset myvar2
echo "Default colon-tilde: ${myvar2:-~:~/bin}"
echo "Assign default with colon-tilde"
unset myvar3
echo "Assign colon-tilde: ${myvar3:=~:~/bin}"
echo "myvar3 after := ${myvar3}"
@@ -0,0 +1,77 @@
name: "Variable expansion"
cases:
- name: "Undefined variables"
stdin: |
echo "Undefined: ${undefined}"
echo "Undefined: $undefined"
- name: "Defined variables"
stdin: |
var=xyz
echo "Defined: ${var}"
echo "Defined: var"
echo "Defined: prefix${var}suffix"
echo "Defined: prefix$var"
- name: "Undefined positional params"
stdin: |
echo "Param: $9"
echo "Param: ${9}"
- name: "High-numbered positional params"
stdin: |
echo "Param: $99"
echo "Param: ${99}"
- name: "Nameref indirect expansion returns target name"
known_failure: true
stdin: |
target="the_value"
declare -n ref=target
echo "indirect: ${!ref}"
echo "direct: $ref"
- name: "Nameref with string operations"
known_failure: true
stdin: |
target="Hello World"
declare -n ref=target
echo "upper: ${ref^^}"
echo "lower: ${ref,,}"
echo "length: ${#ref}"
echo "substring: ${ref:0:5}"
echo "replace: ${ref/World/Bash}"
- name: "Nameref with default value expansion"
known_failure: true
stdin: |
declare -n ref=unset_var
echo "default: ${ref:-default_value}"
echo "assign default: ${ref:=assigned}"
echo "unset_var: $unset_var"
- name: "Nameref with error expansion"
known_failure: true
ignore_stderr: true
stdin: |
declare -n ref=unset_var
echo "${ref:?should error}" 2>/dev/null || echo "caught error"
- name: "Nameref with alternative expansion"
known_failure: true
stdin: |
target="exists"
declare -n ref=target
echo "${ref:+alternative}"
unset target
echo "'${ref:+alternative}'"
- name: "Nameref with pattern removal"
known_failure: true
stdin: |
target="/usr/local/bin/bash"
declare -n ref=target
echo "## */: ${ref##*/}"
echo "%% /*: ${ref%%/*}"
echo "# */: ${ref#*/}"
echo "% /*: ${ref%/*}"