706 lines
22 KiB
YAML
706 lines
22 KiB
YAML
name: "Extended tests"
|
|
cases:
|
|
- name: "Extended test with redirect"
|
|
stdin: |
|
|
[[ $(echo hello >&2) ]] 2>/dev/null
|
|
echo "Result: $?"
|
|
|
|
- name: "File extended tests"
|
|
stdin: |
|
|
[[ -a /tmp ]] && echo "-a correctly checked /tmp"
|
|
[[ -a /some/non/existent/path ]] || echo "-a correctly checked non-existent path"
|
|
|
|
[[ -c /dev/null ]] && echo "-c correctly checked /dev/null"
|
|
[[ -c /tmp ]] || echo "-c correctly checked /tmp"
|
|
|
|
- name: "Existence tests"
|
|
stdin: |
|
|
[[ -e '' ]] || echo "-e correctly identified empty string as non-existent"
|
|
|
|
[[ -e non-existent ]] || echo "-e correctly identified non-existent path"
|
|
[[ ! -e non-existent ]] && echo "! -e correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
[[ -e test-file ]] && echo "-e correctly identified existing non-dir"
|
|
|
|
mkdir test-dir
|
|
[[ -e test-dir ]] && echo "-d correctly identified existing dir"
|
|
|
|
- name: "Directory tests"
|
|
stdin: |
|
|
[[ -d '' ]] || echo "-d correctly identified empty string as non-existent"
|
|
|
|
[[ -d non-existent ]] || echo "-d correctly identified non-existent path"
|
|
[[ ! -d non-existent ]] && echo "! -d correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
[[ -d test-file ]] || echo "-d correctly identified non-dir file"
|
|
|
|
mkdir test-dir
|
|
[[ -d test-dir ]] && echo "-d correctly identified existing dir"
|
|
|
|
- name: "File regular tests"
|
|
stdin: |
|
|
[[ -f '' ]] || echo "-f correctly identified empty string as non-existent"
|
|
|
|
[[ -f non-existent ]] || echo "-f correctly identified non-existent path"
|
|
[[ ! -f non-existent ]] && echo "! -f correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
[[ -f test-file ]] && echo "-f correctly identified regular file"
|
|
|
|
mkdir test-dir
|
|
[[ -f test-dir ]] || echo "-f correctly identified directory as non-regular file"
|
|
|
|
- name: "File symbolic link tests"
|
|
stdin: |
|
|
[[ -L non-existent ]] || echo "-L correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
[[ -L test-file ]] || echo "-L correctly identified non-link file"
|
|
|
|
ln -s test-file valid-link
|
|
[[ -L valid-link ]] && echo "-L correctly identified valid symbolic link"
|
|
|
|
ln -s non-existent-target dangling-link
|
|
[[ -L dangling-link ]] && echo "-L correctly identified dangling symbolic link"
|
|
|
|
- name: "File sticky bit tests"
|
|
stdin: |
|
|
touch test-file
|
|
[[ -k test-file ]] || echo "-k correctly identified file without sticky bit"
|
|
|
|
chmod 1600 test-file
|
|
[[ -k test-file ]] && echo "-k correctly identified file with sticky bit"
|
|
|
|
- name: "Fifo test"
|
|
stdin: |
|
|
touch test-file
|
|
[[ -p test-file ]] || echo "-p correctly identified non-fifo file"
|
|
|
|
mkfifo fifo-file
|
|
[[ -p test-file ]] && echo "-p correctly identified fifo file"
|
|
|
|
- name: "File executable tests"
|
|
stdin: |
|
|
[[ -x non-existent ]] || echo "-x correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
ln -sf link test-file
|
|
[[ -x test-file ]] || echo "-x correctly identified non-executable file"
|
|
[[ -x link ]] || echo "-x correctly identified link to non-executable file"
|
|
|
|
chmod o+x test-file
|
|
[[ -x test-file ]] || echo "-x correctly identified other-only-executable file"
|
|
[[ -x link ]] || echo "-x correctly identified link to other-only-executable file"
|
|
|
|
chmod a+x test-file
|
|
[[ -x test-file ]] && echo "-x correctly identified executable file"
|
|
[[ -x link ]] && echo "-x correctly identified link to executable file"
|
|
|
|
- name: "File writable tests"
|
|
stdin: |
|
|
[[ -w non-existent ]] || echo "-w correctly identified non-existent path"
|
|
|
|
touch test-file
|
|
ln -sf link test-file
|
|
[[ -w test-file ]] || echo "-w correctly identified non-writable file"
|
|
[[ -w link ]] || echo "-w correctly identified link to non-writable file"
|
|
|
|
chmod o+x test-file
|
|
[[ -w test-file ]] || echo "-w correctly identified other-only-writable file"
|
|
[[ -w link ]] || echo "-w correctly identified link to other-only-writable file"
|
|
|
|
chmod a+x test-file
|
|
[[ -w test-file ]] && echo "-w correctly identified writable file"
|
|
[[ -w link ]] && echo "-w correctly identified link to writable file"
|
|
|
|
- name: "Variable set and nameref tests"
|
|
stdin: |
|
|
foo="bar"
|
|
declare -n bang=foo
|
|
[[ -R bang ]] && echo "-R correctly identified nameref variable that is set"
|
|
|
|
declare -n somevar
|
|
[[ -R somevar ]] || echo "-R correctly identified nameref variable that isn't set"
|
|
|
|
- name: "Files refer to same device and inode tests"
|
|
stdin: |
|
|
[[ /bin/sh -ef /bin/sh ]] && echo "-ef correctly identified device and inode numbers"
|
|
|
|
[[ ! /etc/os-release -ef /bin/sh ]] && echo "-ef correctly identified device and inode numbers that do not match"
|
|
|
|
- name: "File is newer"
|
|
stdin: |
|
|
touch -d "2 hours ago" bar
|
|
touch foo
|
|
|
|
[[ foo -nt bar ]] && echo "-nt correctly identified newer file"
|
|
[[ foo -nt foo ]] && echo "-nt incorrectly identified file as newer than itself"
|
|
[[ foo -nt file_no_exists ]] && echo "-nt correctly identified when file2 does not exist"
|
|
|
|
- name: "File is older"
|
|
stdin: |
|
|
touch -d "2 hours ago" foo
|
|
touch bar
|
|
|
|
[[ foo -ot bar ]] && echo "-ot correctly identified older file"
|
|
[[ foo -ot foo ]] && echo "-ot incorrectly identified file as older than itself"
|
|
[[ file_no_exists -ot foo ]] && echo "-ot correctly identified when file1 does not exist"
|
|
|
|
- name: "Unary string extended tests"
|
|
stdin: |
|
|
[[ -z "" ]] && echo "-z: Pass"
|
|
[[ -z "something" ]] && echo "-z: Fail"
|
|
|
|
[[ -n "something" ]] && echo "-n: Pass"
|
|
[[ -n "" ]] && echo "-n: Fail"
|
|
|
|
- name: "Shell option extended tests"
|
|
stdin: |
|
|
set -o emacs
|
|
[[ -o emacs ]] && echo "1: option enabled"
|
|
|
|
set +o emacs
|
|
[[ -o emacs ]] && echo "2: option enabled"
|
|
|
|
- name: "Binary string extended tests"
|
|
stdin: |
|
|
[[ "" == "" ]] && echo "1. Pass"
|
|
[[ "" = "" ]] && echo "2. Pass"
|
|
[[ "" != "" ]] && echo "3. Fail"
|
|
|
|
[[ "a" != "b" ]] && echo "4. Pass"
|
|
[[ "a" = "b" ]] && echo "5. Fail"
|
|
[[ "a" == "b" ]] && echo "6. Fail"
|
|
|
|
[[ "a" < "b" ]] && echo "7. Pass"
|
|
[[ "a" < "a" ]] && echo "8. Fail"
|
|
[[ "b" < "a" ]] && echo "9. Fail"
|
|
[[ "a" > "b" ]] && echo "10. Fail"
|
|
[[ "a" > "a" ]] && echo "11. Fail"
|
|
[[ "b" > "a" ]] && echo "12. Pass"
|
|
|
|
- name: "Binary string matching"
|
|
stdin: |
|
|
[[ "abc" == a* ]] && echo "1. Pass"
|
|
[[ "abc" != a* ]] && echo "2. Fail"
|
|
[[ a* != "abc" ]] && echo "3. Pass"
|
|
|
|
- name: "Binary string matching with expansion"
|
|
stdin: |
|
|
exclude="0123456789"
|
|
[[ "clue" == +([$exclude]) ]] && echo "1. Fail"
|
|
[[ "8675309" == +([$exclude]) ]] && echo "2. Pass"
|
|
|
|
- name: "Quoted pattern binary string matching"
|
|
stdin: |
|
|
[[ "abc" == "a*" ]] && echo "1. Matches"
|
|
[[ "abc" != "a*" ]] && echo "2. Matches"
|
|
|
|
- name: "Tilde binary string matching"
|
|
stdin: |
|
|
x='~/'
|
|
[[ $x == ~* ]] && echo "1. Matches"
|
|
|
|
- name: "Arithmetic extended tests"
|
|
stdin: |
|
|
[[ 0 -eq 0 ]] && echo "1. Pass"
|
|
[[ 0 -ne 0 ]] && echo "2. Fail"
|
|
[[ 0 -lt 0 ]] && echo "3. Fail"
|
|
[[ 0 -le 0 ]] && echo "4. Pass"
|
|
[[ 0 -gt 0 ]] && echo "5. Fail"
|
|
[[ 0 -ge 0 ]] && echo "6. Pass"
|
|
|
|
[[ 0 -eq 1 ]] && echo "7. Fail"
|
|
[[ 0 -ne 1 ]] && echo "8. Pass"
|
|
[[ 0 -lt 1 ]] && echo "9. Pass"
|
|
[[ 0 -le 1 ]] && echo "10. Pass"
|
|
[[ 0 -gt 1 ]] && echo "11. Fail"
|
|
[[ 0 -ge 1 ]] && echo "12. Fail"
|
|
|
|
[[ 1 -eq 0 ]] && echo "13. Fail"
|
|
[[ 1 -ne 0 ]] && echo "14. Pass"
|
|
[[ 1 -lt 0 ]] && echo "15. Fail"
|
|
[[ 1 -le 0 ]] && echo "16. Fail"
|
|
[[ 1 -gt 0 ]] && echo "17. Pass"
|
|
[[ 1 -ge 0 ]] && echo "18. Pass"
|
|
|
|
- name: "Regex"
|
|
stdin: |
|
|
[[ "a" =~ ^a$ ]] && echo "1. Pass"
|
|
[[ "abc" =~ a* ]] && echo "2. Pass"
|
|
[[ a =~ ^(a)$ ]] && echo "3. Pass"
|
|
[[ a =~ ^(a|b)$ ]] && echo "4. Pass"
|
|
[[ a =~ c ]] && echo "5. Pass"
|
|
|
|
# A ']' in the first member position (after any '^') is a member of the bracket
|
|
# expression, not its terminator.
|
|
- name: "Regex with leading ] in bracket expression"
|
|
stdin: |
|
|
[[ "]" =~ ^[]]$ ]] && echo "1. Matched"
|
|
[[ "a" =~ ^[]a]$ ]] && echo "2. Matched"
|
|
[[ "[" =~ ^[][]$ ]] && echo "3. Matched"
|
|
[[ "]" =~ ^[][]$ ]] && echo "4. Matched"
|
|
[[ "a" =~ ^[][]$ ]] || echo "5. Not matched"
|
|
[[ "[" =~ ^[^][]$ ]] || echo "6. Not matched"
|
|
[[ "a" =~ ^[^][]$ ]] && echo "7. Matched"
|
|
[[ "[" =~ ^[]a[]$ ]] && echo "8. Matched"
|
|
# Only the first '^' negates; the second is an ordinary member, so the ']'
|
|
# that follows it does terminate the expression.
|
|
[[ "xa" =~ ^[^^][ab]$ ]] && echo "9. Matched"
|
|
[[ "x[" =~ ^[^^][ab]$ ]] || echo "10. Not matched"
|
|
[[ "x]" =~ ^[^^]]$ ]] && echo "11. Matched"
|
|
# That leading ']' can also be the low end of a range.
|
|
[[ "^" =~ ^[]-a]$ ]] && echo "12. Matched"
|
|
[[ "]" =~ ^[]-a]$ ]] && echo "13. Matched"
|
|
[[ "-" =~ ^[]-a]$ ]] || echo "14. Not matched"
|
|
[[ "b" =~ ^[]-a]$ ]] || echo "15. Not matched"
|
|
echo "done"
|
|
|
|
# `fancy_regex` and POSIX ERE disagree on how a '-' range endpoint binds, and
|
|
# brush passes the user's bracket expression through without normalizing it.
|
|
- name: "Regex with '-' as a range endpoint"
|
|
known_failure: true
|
|
stdin: |
|
|
for s in '+' ',' '-' '.' '/' 'a'; do
|
|
[[ "$s" =~ ^[+--]$ ]] && echo "1. matched: $s"
|
|
done
|
|
for s in '+' ',' '-' '.' '/' 'a'; do
|
|
[[ "$s" =~ ^[--/]$ ]] && echo "2. matched: $s"
|
|
done
|
|
echo "done"
|
|
|
|
# A reversed range is rejected by both shells, but bash reports a bad regex as
|
|
# a usage error (status 2) while brush treats it as a failed match (status 1).
|
|
- name: "Regex with a reversed range"
|
|
known_failure: true
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ x =~ ^[9-0]$ ]]
|
|
echo "status=$?"
|
|
|
|
# POSIX ERE reads '?*' as two stacked quantifiers; `fancy_regex` rejects the
|
|
# second as having no valid target.
|
|
- name: "Regex with stacked quantifiers"
|
|
known_failure: true
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ "]" =~ ^[a]?*]$ ]]
|
|
echo "status=$?"
|
|
|
|
- name: "Regex with case insensitivity"
|
|
stdin: |
|
|
shopt -u nocasematch
|
|
[[ "a" =~ A ]] && echo "1. Pass"
|
|
|
|
shopt -s nocasematch
|
|
[[ "a" =~ A ]] && echo "1. Pass"
|
|
|
|
- name: "Regex with capture"
|
|
stdin: |
|
|
pattern='(Hello), ([a-z]+)\.'
|
|
if [[ "Hello, world." =~ ${pattern} ]]; then
|
|
echo "Match found!"
|
|
for i in "${!BASH_REMATCH[@]}"; do
|
|
echo "$i: '${BASH_REMATCH[$i]}'"
|
|
done
|
|
fi
|
|
|
|
- name: "Regex with capture including optional matches"
|
|
stdin: |
|
|
pattern='(Hello)(,?) ([a-z]+)\.'
|
|
if [[ "Hello world." =~ ${pattern} ]]; then
|
|
echo "Match found!"
|
|
for i in "${!BASH_REMATCH[@]}"; do
|
|
echo "$i: '${BASH_REMATCH[$i]}'"
|
|
done
|
|
fi
|
|
|
|
- name: "Regex with quoting"
|
|
stdin: |
|
|
regex="^$"
|
|
|
|
# TODO(test): The commented out lines appear differ in behavior between versions of bash.
|
|
# [[ "" =~ '' ]] && echo "1. Matched"
|
|
[[ "" =~ '^$' ]] && echo "2. Matched"
|
|
# [[ "" =~ "" ]] && echo "3. Matched"
|
|
[[ "" =~ "^$" ]] && echo "4. Matched"
|
|
[[ "" =~ ^$ ]] && echo "5. Matched"
|
|
[[ "" =~ ${regex} ]] && echo "6. Matched"
|
|
[[ "" =~ "${regex}" ]] && echo "7. Matched"
|
|
|
|
- name: "Regex with escaping"
|
|
stdin: |
|
|
[[ '' =~ ^\$$ ]] && echo "1. Matched"
|
|
|
|
- name: "Regex with double parens"
|
|
stdin: |
|
|
if [[ xy =~ x+((y)) ]]; then
|
|
echo "Matches"
|
|
fi
|
|
|
|
- name: "Regex with special chars in parens"
|
|
stdin: |
|
|
[[ "<" =~ (<) ]] && echo "1. Matched"
|
|
[[ ">" =~ (<) ]] && echo "2. Matched"
|
|
|
|
- name: "Regex with spaces inside parens"
|
|
stdin: |
|
|
[[ "x" =~ (a| *) ]] && echo "1. Matched"
|
|
[[ "a b" =~ ^(a |b) ]] && echo "2. Matched: [${BASH_REMATCH[0]}]"
|
|
[[ "a b" =~ ^(a |b)$ ]] || echo "3. No match"
|
|
[[ " " =~ ^( )$ ]] && echo "4. Matched: [${BASH_REMATCH[1]}]"
|
|
|
|
- name: "Regex with unescaped open bracket in character class"
|
|
stdin: |
|
|
[[ "[" =~ ^([x[]) ]] && echo "Matched"
|
|
|
|
- name: "Empty and space checks"
|
|
stdin: |
|
|
check() {
|
|
var="$1"
|
|
[[ ${var} && ! ${var//[[:space:]]/} ]]
|
|
}
|
|
|
|
check "" && echo "1. Only space"
|
|
check " " && echo "2. Only space"
|
|
check $'\t' && echo "3. Only space"
|
|
check " a " && echo "4. Only space"
|
|
|
|
- name: "Newlines in test expression"
|
|
stdin: |
|
|
[[
|
|
"a" == "a"
|
|
&&
|
|
"b" == "b"
|
|
]] && echo "Succeeded"
|
|
|
|
- name: "Variable set checks"
|
|
stdin: |
|
|
declare set_but_no_value
|
|
[[ -v set_but_no_value ]] && echo "1. Set but no value"
|
|
|
|
declare set_with_value=xyz
|
|
[[ -v set_with_value ]] && echo "2. Set with value"
|
|
|
|
[[ -v not_set ]] || echo "3. Not set"
|
|
|
|
- name: "Variable set checks on array elements"
|
|
known_failure: true # TODO(extended-tests): -v with a subscript always reports an array element as unset
|
|
stdin: |
|
|
indexed=(x y)
|
|
[[ -v indexed[1] ]] && echo "1. Indexed element set"
|
|
[[ -v indexed[5] ]] || echo "2. Indexed element unset"
|
|
|
|
declare -A assoc=([key]=value)
|
|
[[ -v assoc[key] ]] && echo "3. Associative element set"
|
|
[[ -v assoc[nope] ]] || echo "4. Associative element unset"
|
|
|
|
test -v 'indexed[0]' && echo "5. test -v sees the element"
|
|
|
|
- name: "Variables in extended tests"
|
|
stdin: |
|
|
var=10
|
|
|
|
[[ $var -eq 10 ]] && echo "1. Pass"
|
|
[[ var -eq 10 ]] && echo "2. Pass"
|
|
|
|
- name: "Regex with min/max counts"
|
|
stdin: |
|
|
[[ z =~ ^z{2,6}$ ]] && echo "1. Matches"
|
|
[[ zzzz =~ ^z{2,6}$ ]] && echo "2. Matches"
|
|
[[ zzzzzzzzz =~ ^z{2,6}$ ]] && echo "3. Matches"
|
|
|
|
- name: "Regex with newline"
|
|
stdin: |
|
|
[[ $'\n' =~ . ]] && echo "1. Matches"
|
|
|
|
- name: "Arithmetic comparisons with whitespace in operands"
|
|
stdin: |
|
|
# Leading/trailing whitespace should be trimmed before comparison
|
|
[[ 0 -eq ' 0' ]] && echo "1. leading space"
|
|
[[ 0 -eq '0 ' ]] && echo "2. trailing space"
|
|
[[ 0 -eq ' 0 ' ]] && echo "3. both spaces"
|
|
|
|
# Tabs
|
|
[[ 0 -eq ' 0' ]] && echo "4. leading tab"
|
|
[[ 0 -eq '0 ' ]] && echo "5. trailing tab"
|
|
|
|
# Newlines
|
|
[[ 0 -eq ' 0
|
|
' ]] && echo "6. newline"
|
|
|
|
# Other operators
|
|
[[ 5 -lt ' 10' ]] && echo "7. -lt with space"
|
|
[[ 10 -gt ' 5' ]] && echo "8. -gt with space"
|
|
[[ 5 -ne ' 6' ]] && echo "9. -ne with space"
|
|
[[ 5 -le ' 5' ]] && echo "10. -le with space"
|
|
[[ 5 -ge ' 5' ]] && echo "11. -ge with space"
|
|
|
|
# Negative numbers with whitespace
|
|
[[ -5 -eq ' -5' ]] && echo "12. negative with space"
|
|
|
|
- name: "Terminal fd check with whitespace"
|
|
stdin: |
|
|
# -t operator should trim whitespace before parsing fd number
|
|
# These should all behave identically (false since not a terminal)
|
|
[[ -t 0 ]]
|
|
r1=$?
|
|
[[ -t ' 0' ]]
|
|
r2=$?
|
|
[[ -t '0 ' ]]
|
|
r3=$?
|
|
|
|
# All should have the same result
|
|
[[ $r1 -eq $r2 && $r2 -eq $r3 ]] && echo "All -t results match"
|
|
|
|
- name: "Nameref -R with attribute removal"
|
|
stdin: |
|
|
target="value"
|
|
declare -n ref=target
|
|
[[ -R ref ]] && echo "ref is a nameref"
|
|
[[ -R target ]] && echo "target is a nameref" || echo "target is not a nameref"
|
|
declare +n ref
|
|
[[ -R ref ]] && echo "still nameref" || echo "no longer a nameref"
|
|
|
|
- name: "Nameref -v follows nameref"
|
|
known_failure: true
|
|
stdin: |
|
|
target="value"
|
|
declare -n ref=target
|
|
[[ -v ref ]] && echo "ref is set (via target)"
|
|
unset target
|
|
[[ -v ref ]] && echo "ref still set" || echo "ref not set (target gone)"
|
|
|
|
- name: "Nameref -v to array element treats resolved name literally"
|
|
known_failure: true
|
|
stdin: |
|
|
arr=(a b c d)
|
|
declare -n ref='arr[2]'
|
|
[[ -v ref ]] && echo "set" || echo "unset"
|
|
|
|
- name: "Nameref -v with explicit subscript on nameref to whole array"
|
|
# TODO(nameref): brush doesn't yet handle [[ -v "ref[N]" ]] where ref is
|
|
# a nameref to a whole array. Bash resolves ref→arr and then checks arr[2],
|
|
# but brush currently treats the subscript as part of the nameref target
|
|
# string rather than applying it after resolution. The fix belongs in
|
|
# extendedtests.rs (ShellVariableIsSetAndAssigned) — the operand "ref[2]"
|
|
# needs to be split into name="ref" + subscript="2", the nameref resolved
|
|
# on the name portion, then the subscript applied to the resolved target.
|
|
known_failure: true
|
|
stdin: |
|
|
arr=(a b c d)
|
|
declare -n ref2=arr
|
|
[[ -v "ref2[2]" ]] && echo "set" || echo "unset"
|
|
|
|
- name: "Nameref -v to nonexistent array index"
|
|
known_failure: true
|
|
stdin: |
|
|
arr=(a b c)
|
|
declare -n ref='arr[5]'
|
|
[[ -v ref ]] && echo "set" || echo "unset"
|
|
|
|
#
|
|
# Newlines within [[ ]] conditional expressions.
|
|
#
|
|
# bash allows newlines (1) at the start of a term (after `[[`, `(`, `&&`,
|
|
# `||`, `!`) and (2) trailing after a complete binary/unary/parenthesized
|
|
# term -- but NOT after a bare-word string test. These cases pin down both
|
|
# the accepted and rejected forms. See issue #1176.
|
|
#
|
|
|
|
- name: "Newline after ( in [[ ]]"
|
|
stdin: |
|
|
[[ (
|
|
a = a ) ]] && echo ok
|
|
|
|
- name: "Newline after ( before bare word in [[ ]]"
|
|
stdin: |
|
|
[[ (
|
|
a ) ]] && echo ok
|
|
|
|
- name: "Newline before ) after binary test in [[ ]]"
|
|
stdin: |
|
|
[[ ( a = a
|
|
) ]] && echo ok
|
|
|
|
- name: "Newline before ) after unary test in [[ ]]"
|
|
stdin: |
|
|
[[ ( -n a
|
|
) ]] && echo ok
|
|
|
|
- name: "Newline before ) after nested parens in [[ ]]"
|
|
stdin: |
|
|
[[ ( ( a = a )
|
|
) ]] && echo ok
|
|
|
|
- name: "Multiple newlines before ) in [[ ]]"
|
|
stdin: |
|
|
[[ ( a = a
|
|
|
|
|
|
|
|
) ]] && echo ok
|
|
|
|
- name: "Newline after && in [[ ]]"
|
|
stdin: |
|
|
[[ ( a = a &&
|
|
b = b ) ]] && echo ok
|
|
|
|
- name: "Newline before && after binary test in [[ ]]"
|
|
stdin: |
|
|
[[ a = a
|
|
&& b = b ]] && echo ok
|
|
|
|
- name: "Newline before || after binary test in [[ ]]"
|
|
stdin: |
|
|
[[ a = b
|
|
|| b = b ]] && echo ok
|
|
|
|
- name: "Leading newline after [[ "
|
|
stdin: |
|
|
[[
|
|
a = a ]] && echo ok
|
|
|
|
- name: "Newline after ! in [[ ]]"
|
|
stdin: |
|
|
[[ !
|
|
a = b ]] && echo ok
|
|
|
|
- name: "Newline before ]] after binary test"
|
|
stdin: |
|
|
[[ a = a
|
|
]] && echo ok
|
|
|
|
- name: "Newline before ]] after unary test"
|
|
stdin: |
|
|
[[ -n a
|
|
]] && echo ok
|
|
|
|
- name: "Newline before ]] after parenthesized expression"
|
|
stdin: |
|
|
[[ ( a = a )
|
|
]] && echo ok
|
|
|
|
- name: "Newline before ]] after not of parenthesized expression"
|
|
stdin: |
|
|
[[ ! ( a = b )
|
|
]] && echo ok
|
|
|
|
# Cases bash rejects as syntax errors: a newline may not follow a bare-word
|
|
# string test (whether before `]]`, `)`, or a binary/logical operator).
|
|
|
|
- name: "Error: newline before ]] after bare word"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ a
|
|
]] && echo should-not-print
|
|
|
|
- name: "Error: newline before ) after bare word"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ ( a
|
|
) ]] && echo should-not-print
|
|
|
|
- name: "Error: newline before && after bare word"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ a
|
|
&& b ]] && echo should-not-print
|
|
|
|
- name: "Error: newline before ) after not of bare word"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ ( ! a
|
|
) ]] && echo should-not-print
|
|
|
|
# POSIX regexes have no line anchors: ^ and $ match only at the ends of the
|
|
# whole string, while . matches any character including newline.
|
|
- name: "Regex anchors match the whole string, not lines"
|
|
stdin: |
|
|
s=$'a\nb'
|
|
[[ $s =~ ^b ]] && echo "wrong 1" || echo "ok 1"
|
|
[[ $s =~ a$ ]] && echo "wrong 2" || echo "ok 2"
|
|
[[ $s =~ ^a.b$ ]] && echo "ok 3"
|
|
t=$' \t\nabc \t\n'
|
|
re=$'[ \t\n]+$'
|
|
[[ $t =~ $re ]] && printf '[%s]\n' "${t::${#t}-${#BASH_REMATCH}}"
|
|
|
|
# An unquoted unary operator at the start of a test is read as the operator and
|
|
# takes the next word as its operand, even a word that looks like an operator.
|
|
- name: "Unary operator takes an operator-like word as operand"
|
|
stdin: |
|
|
[[ -z == ]]; echo "1: $?"
|
|
[[ -n == ]]; echo "2: $?"
|
|
[[ -z -z ]]; echo "3: $?"
|
|
[[ -n -eq ]]; echo "4: $?"
|
|
[[ -z =~ ]]; echo "5: $?"
|
|
[[ -z ! ]]; echo "6: $?"
|
|
[[ -f -nt ]]; echo "7: $?"
|
|
[[ ! -z == ]]; echo "8: $?"
|
|
[[ ( -z != ) ]]; echo "9: $?"
|
|
[[ -n x && -z != ]]; echo "10: $?"
|
|
[[ -z == || -n = ]]; echo "11: $?"
|
|
|
|
- name: "Quoted or expanded unary operator is a plain word"
|
|
stdin: |
|
|
[[ "-z" == x ]]; echo "1: $?"
|
|
[[ '-z' == -z ]]; echo "2: $?"
|
|
[[ \-z == -z ]]; echo "3: $?"
|
|
x=-z
|
|
[[ $x == -z ]]; echo "4: $?"
|
|
[[ $x ]]; echo "5: $?"
|
|
[[ x == -z ]]; echo "6: $?"
|
|
[[ -zz == x ]]; echo "7: $?"
|
|
|
|
- name: "Error: word after unary operand of [[ ]]"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -z == x ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator as left side of binary test"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -f -nt x ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator as left side of arithmetic test"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -n -eq 1 ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator as left side of < test"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -z < x ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator as left side of =~ test"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -z =~ x ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator followed by ||"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -n || x ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator followed by ]]"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -z ]] && echo should-not-print
|
|
|
|
- name: "Error: unary operator followed by ) in [[ ]]"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ ( -z ) ]] && echo should-not-print
|
|
|
|
- name: "Error: negated unary operator followed by ]]"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ ! -n ]] && echo should-not-print
|
|
|
|
- name: "Error: ]] as unary operand"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
[[ -z ]] ]] && echo should-not-print
|