Merge nucleic/sleek-thistle-egret-fyej into dev
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
name: "Builtins: alias"
|
||||
cases:
|
||||
- name: "Basic alias usage"
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
alias myalias=echo
|
||||
alias
|
||||
myalias 'hello'
|
||||
|
||||
- name: "Alias with trailing space"
|
||||
known_failure: true # Issue #57
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
alias cmd='echo '
|
||||
alias other='replaced '
|
||||
alias otherother='also-replaced'
|
||||
|
||||
cmd other otherother
|
||||
|
||||
- name: "Alias referencing to alias"
|
||||
known_failure: true # Issue #57
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
alias myalias=echo
|
||||
alias outeralias=myalias
|
||||
outeralias 'hello'
|
||||
|
||||
- name: "Alias to keywords"
|
||||
known_failure: true # Issue #286
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
alias myalias=if
|
||||
myalias true; then echo "true"; fi
|
||||
@@ -0,0 +1,160 @@
|
||||
name: "Builtins: bind"
|
||||
cases:
|
||||
- name: "bind succeeds in non-interactive mode"
|
||||
stdin: |
|
||||
bind > /dev/null 2>&1
|
||||
echo "exit: $?"
|
||||
|
||||
- name: "bind -l"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
ignore_stderr: true
|
||||
min_oracle_version: "5.3" # Added functions in 5.3
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
# List bindable actions but ignore brush custom entries
|
||||
echo "[bind -l]"
|
||||
bind -l | grep -v brush- | sort
|
||||
echo "bind -l result: ${PIPESTATUS[@]}"
|
||||
|
||||
- name: "bind -q/-P/-p"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
# Basic-check some known bindings
|
||||
well_known_func="clear-screen"
|
||||
|
||||
echo "[bind -q]"
|
||||
bind -q ${well_known_func}
|
||||
echo "bind -q result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -P]"
|
||||
bind -P | grep ${well_known_func}
|
||||
echo "bind -P result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -p]"
|
||||
bind -p | grep ${well_known_func}
|
||||
echo "bind -p result: ${PIPESTATUS[@]}"
|
||||
|
||||
- name: "bind -x/-X/-r/-u"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
min_oracle_version: "5.3" # -X output changed in 5.3
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
# Bind a key and check it
|
||||
echo "[bind -x]"
|
||||
bind -x '"\C-t":echo hi'
|
||||
echo "bind -x result: $?"
|
||||
|
||||
echo "[bind -X]"
|
||||
bind -X
|
||||
echo "bind -X result: $?"
|
||||
|
||||
# Unbind it via -r
|
||||
echo "[bind -r]"
|
||||
bind -r '\C-t'
|
||||
echo "bind -r result: $?"
|
||||
bind -X
|
||||
echo "bind -X result: $?"
|
||||
echo "[bind -r (again)]"
|
||||
bind -r '\C-t'
|
||||
echo "bind -r (again) result: $?"
|
||||
|
||||
# Unbind via -u
|
||||
well_known_func="clear-screen"
|
||||
echo "[bind -u]"
|
||||
bind -u ${well_known_func}
|
||||
echo "bind -u result: $?"
|
||||
echo "[bind -q (after -u)]"
|
||||
bind -q ${well_known_func}
|
||||
echo "bind -q (after -u) result: $?"
|
||||
|
||||
- name: "bind keyseq"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
well_known_func="clear-screen"
|
||||
bind -u ${well_known_func} # Ensure it's unbound first
|
||||
|
||||
# Bind a keyseq
|
||||
echo "[bind keyseq]"
|
||||
bind '"\C-t":clear-screen'
|
||||
echo "bind keyseq result: $?"
|
||||
|
||||
# List current bindings
|
||||
bind -q ${well_known_func}
|
||||
echo "bind -q result: $?"
|
||||
|
||||
- name: "bind macro 1"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
key_to_bind='\C-r'
|
||||
|
||||
echo "[bind -S (pre)]"
|
||||
bind -S | grep "${key_to_bind}"
|
||||
echo "bind -S result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -s (pre)]"
|
||||
bind -s | grep "${key_to_bind}"
|
||||
echo "bind -s result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind macro]"
|
||||
bind '"\C-r": "\e[0;1A\e[0;0A"'
|
||||
echo "bind macro result: $?"
|
||||
|
||||
echo "[bind -S]"
|
||||
bind -S | grep "${key_to_bind}"
|
||||
echo "bind -S result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -s]"
|
||||
bind -s | grep "${key_to_bind}"
|
||||
echo "bind -s result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -r]"
|
||||
bind -r "${key_to_bind}"
|
||||
echo "bind -r result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -s (after -r)]"
|
||||
bind -s | grep "${key_to_bind}"
|
||||
echo "bind -s (after -r) result: ${PIPESTATUS[@]}"
|
||||
|
||||
echo "[bind -S (after -r)]"
|
||||
bind -S | grep "${key_to_bind}"
|
||||
echo "bind -S (after -r) result: ${PIPESTATUS[@]}"
|
||||
|
||||
- name: "bind macro 2"
|
||||
args: ["run.sh"]
|
||||
removed_default_args: ["--input-backend=basic"]
|
||||
additional_test_args: ["--input-backend=reedline"]
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "run.sh"
|
||||
contents: |
|
||||
key_to_bind='\ec'
|
||||
|
||||
echo "[bind macro]"
|
||||
bind '"\ec": " \C-b\C-k`something`\e\C-e"'
|
||||
echo "bind macro result: $?"
|
||||
|
||||
echo "[bind -S]"
|
||||
bind -S | grep "${key_to_bind}"
|
||||
echo "bind -S result: ${PIPESTATUS[@]}"
|
||||
@@ -0,0 +1,44 @@
|
||||
name: "Builtins: builtin"
|
||||
cases:
|
||||
- name: "builtin with no builtin"
|
||||
stdin: builtin
|
||||
|
||||
- name: "builtin with unknown builtin"
|
||||
ignore_stderr: true
|
||||
stdin: builtin not-a-builtin args
|
||||
|
||||
- name: "valid builtin"
|
||||
stdin: builtin echo "Hello world"
|
||||
|
||||
- name: "valid builtin with hyphen args"
|
||||
stdin: builtin echo -e "Hello\nWorld"
|
||||
|
||||
- name: "builtin passing through results"
|
||||
stdin: |
|
||||
builtin false; echo "builtin false => $?"
|
||||
builtin true; echo "builtin true => $?"
|
||||
|
||||
- name: "builtin with non-decl builtin"
|
||||
stdin: |
|
||||
builtin echo variable=value
|
||||
|
||||
- name: "builtin with decl builtin"
|
||||
stdin: |
|
||||
builtin declare variable=value
|
||||
declare -p variable
|
||||
|
||||
- name: "builtin with disabled builtin"
|
||||
ignore_stderr: true
|
||||
incompatible_os:
|
||||
- opensuse-tumbleweed
|
||||
stdin: |
|
||||
cd .
|
||||
echo $?
|
||||
|
||||
enable -n cd
|
||||
builtin cd .
|
||||
echo $?
|
||||
|
||||
enable cd
|
||||
builtin cd .
|
||||
echo $?
|
||||
@@ -0,0 +1,67 @@
|
||||
name: "caller builtin"
|
||||
cases:
|
||||
- name: "caller in function"
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
function outer() {
|
||||
echo "In outer"
|
||||
inner
|
||||
}
|
||||
|
||||
function inner() {
|
||||
echo "In inner"
|
||||
|
||||
echo "[Calling caller 0]"
|
||||
caller 0
|
||||
|
||||
echo "[Calling caller 1]"
|
||||
caller 1
|
||||
}
|
||||
stdin: |
|
||||
source script.sh
|
||||
outer
|
||||
|
||||
- name: "caller no args"
|
||||
known_failure: true # TODO(caller): needs investigation
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
function myfunc() {
|
||||
echo "In myfunc"
|
||||
caller
|
||||
}
|
||||
stdin: |
|
||||
source script.sh
|
||||
myfunc
|
||||
|
||||
- name: "caller no args (nested)"
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
function myfunc() {
|
||||
echo "In myfunc"
|
||||
caller
|
||||
}
|
||||
|
||||
function myouterfunc() {
|
||||
echo "In myouterfunc"
|
||||
myfunc
|
||||
}
|
||||
stdin: |
|
||||
source script.sh
|
||||
myouterfunc
|
||||
|
||||
- name: "caller invalid expr"
|
||||
ignore_stderr: true # Different error messages
|
||||
stdin: |
|
||||
f() { caller x; }
|
||||
f
|
||||
|
||||
- name: "caller out of range"
|
||||
stdin: |
|
||||
caller 100
|
||||
|
||||
- name: "caller not in function"
|
||||
stdin: |
|
||||
caller
|
||||
@@ -0,0 +1,171 @@
|
||||
name: "Builtins: cd"
|
||||
cases:
|
||||
- name: "Basic cd usage"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo "cd /"
|
||||
cd /
|
||||
echo $?
|
||||
echo "pwd: $PWD"
|
||||
|
||||
echo "cd usr"
|
||||
cd usr
|
||||
echo $?
|
||||
echo "pwd: $PWD"
|
||||
|
||||
echo "cd"
|
||||
cd
|
||||
echo $?
|
||||
echo "pwd: $PWD"
|
||||
|
||||
- name: "cd to file"
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "file"
|
||||
contents: "file contents"
|
||||
stdin: |
|
||||
ls -1
|
||||
cd file
|
||||
echo $?
|
||||
ls -1
|
||||
|
||||
- name: "cd -"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
cd /
|
||||
echo "pwd: $PWD"
|
||||
cd usr
|
||||
echo "pwd: $PWD"
|
||||
echo "oldpwd: $OLDPWD"
|
||||
cd -
|
||||
echo $?
|
||||
echo "pwd: $PWD"
|
||||
|
||||
- name: "cd ~"
|
||||
stdin: |
|
||||
mkdir ./my_home
|
||||
export HOME="$(realpath ./my_home)"
|
||||
echo "Set HOME: $(basename $HOME)"
|
||||
(
|
||||
echo "Subshell 1: HOME=$(basename $HOME)"
|
||||
cd ~
|
||||
echo "pwd: $(basename $PWD)"
|
||||
)
|
||||
(
|
||||
echo "Subshell 2: HOME=$(basename $HOME)"
|
||||
cd -L ~
|
||||
echo "pwd: $(basename $PWD)"
|
||||
)
|
||||
(
|
||||
echo "Subshell 3: HOME=$(basename $HOME)"
|
||||
cd -P ~
|
||||
echo "pwd: $(basename $PWD)"
|
||||
)
|
||||
|
||||
- name: "cd with symlink"
|
||||
stdin: |
|
||||
mkdir -p ./level1/level2/level3
|
||||
cd level1
|
||||
ln -s ./level2/level3 ./symlink
|
||||
|
||||
# -L by default
|
||||
(
|
||||
cd ./symlink
|
||||
echo "$(basename $PWD)"
|
||||
cd ..
|
||||
echo "$(basename $PWD)"
|
||||
)
|
||||
|
||||
- name: "cd -L"
|
||||
stdin: |
|
||||
mkdir -p ./level1/level2/level3
|
||||
cd level1
|
||||
ln -s ./level2/level3 ./symlink
|
||||
|
||||
echo "[Case 1]"
|
||||
(
|
||||
cd ./symlink
|
||||
echo "$(basename $PWD)"
|
||||
cd -L ..
|
||||
echo "$(basename $PWD)"
|
||||
)
|
||||
|
||||
echo "[Case 2]"
|
||||
(
|
||||
cd -L ./symlink
|
||||
echo "$(basename $PWD)"
|
||||
cd ..
|
||||
echo "$(basename $PWD)"
|
||||
)
|
||||
|
||||
# without pwd
|
||||
echo "[Case 3]"
|
||||
(
|
||||
cd -L ./symlink
|
||||
echo "$(basename $PWD)"
|
||||
export PWD=
|
||||
cd -L ..
|
||||
echo "$(basename $PWD)"
|
||||
)
|
||||
|
||||
echo "[Case 4]"
|
||||
(
|
||||
cd -L ./symlink
|
||||
export PWD=
|
||||
# start a shell without $PWD
|
||||
(
|
||||
cd .
|
||||
echo "$(basename $PWD)"
|
||||
)
|
||||
)
|
||||
|
||||
- name: "cd -P"
|
||||
stdin: |
|
||||
mkdir -p ./level1/level2/level3
|
||||
cd level1
|
||||
ln -s ./level2/level3 ./symlink
|
||||
|
||||
echo "[Case 1]"
|
||||
(
|
||||
cd ./symlink
|
||||
echo "cd ./symlink => $(basename $PWD)"
|
||||
cd -P ..
|
||||
echo "cd -P .. => $(basename $PWD)"
|
||||
)
|
||||
|
||||
echo "[Case 2]"
|
||||
(
|
||||
cd -P ./symlink
|
||||
echo "cd -P ./symlink => $(basename $PWD)"
|
||||
cd ..
|
||||
echo "cd .. => $(basename $PWD)"
|
||||
)
|
||||
|
||||
- name: "OLDPWD"
|
||||
env:
|
||||
OLDPWD: "/nonexistent/path"
|
||||
stdin: |
|
||||
mkdir subdir
|
||||
|
||||
echo "[0]"
|
||||
echo ${OLDPWD+set}
|
||||
|
||||
echo "[1]"
|
||||
echo ${OLDPWD-NOTSET}
|
||||
|
||||
echo "[2]"
|
||||
declare -p OLDPWD 2>&1 | grep -o "declare -[a-z]* OLDPWD"
|
||||
|
||||
echo "[3]"
|
||||
env | grep "^OLDPWD=" || echo "NOT_IN_ENV"
|
||||
|
||||
echo
|
||||
echo "[Unsetting + cd'ing...]"
|
||||
unset OLDPWD
|
||||
cd $PWD/subdir
|
||||
|
||||
echo "[4]"
|
||||
declare -p OLDPWD 2>&1 | grep -o "declare -[a-z]* OLDPWD"
|
||||
|
||||
echo "[5]"
|
||||
env | grep -o "^OLDPWD="
|
||||
@@ -0,0 +1,13 @@
|
||||
name: "Builtins: colon"
|
||||
cases:
|
||||
- name: "Basic colon usage"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
:
|
||||
echo "Result 1: $?"
|
||||
: something
|
||||
echo "Result 2: $?"
|
||||
: --anything=here or here
|
||||
echo "Result 3: $?"
|
||||
: --help
|
||||
echo "Result 4: $?"
|
||||
@@ -0,0 +1,88 @@
|
||||
name: "Builtins: command"
|
||||
cases:
|
||||
- name: "Basic command usage"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo "Executing echo built-in"
|
||||
command echo "Hello"
|
||||
|
||||
echo "Executing ls using name"
|
||||
command ls -d /
|
||||
|
||||
echo "Executing ls using absolute path"
|
||||
command $(which ls) -d /
|
||||
|
||||
echo "Executing non-existent command by name"
|
||||
command non-existent
|
||||
|
||||
echo "Executing non-existent command by path"
|
||||
command /usr/bin/non-existent
|
||||
|
||||
- name: "Non-executable command"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
mkdir subdir
|
||||
|
||||
echo "Before executing"
|
||||
./subdir
|
||||
echo "After executing: result=$?"
|
||||
|
||||
echo "Before and-or list execution"
|
||||
./subdir || true
|
||||
echo "After and-or list execution: result=$?"
|
||||
|
||||
- name: "command -v"
|
||||
stdin: |
|
||||
echo "PATH: $PATH"
|
||||
|
||||
echo "[echo]"
|
||||
command -v echo
|
||||
|
||||
echo "[non-existent]"
|
||||
command -v non-existent || echo "1. Not found"
|
||||
|
||||
echo "[/usr/bin/non-existent]"
|
||||
command -v /usr/bin/non-existent || echo "2. Not found"
|
||||
|
||||
- name: "command -v -p"
|
||||
stdin: |
|
||||
unset PATH
|
||||
|
||||
echo "[no -p]"
|
||||
command -v cat
|
||||
|
||||
echo "[-p]"
|
||||
command -v -p cat
|
||||
|
||||
- name: "command -v with full paths"
|
||||
stdin: |
|
||||
echo "[cat]"
|
||||
command -v cat
|
||||
|
||||
echo "[\$(command -v cat)]"
|
||||
command -v $(command -v cat)
|
||||
|
||||
- name: "command -V"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
command -V echo
|
||||
command -V ls
|
||||
command -V $(which ls)
|
||||
|
||||
command -V non-existent || echo "1. Not found"
|
||||
command -V /usr/bin/non-existent || echo "2. Not found"
|
||||
|
||||
- name: "command with --"
|
||||
stdin: |
|
||||
command -- ls
|
||||
|
||||
- name: "command with --help"
|
||||
stdin: |
|
||||
command ls --help
|
||||
|
||||
- name: "command with -p"
|
||||
ignore_stderr: true # In case it fails on the oracle as well, ignore the specific error message.
|
||||
stdin: |
|
||||
unset PATH
|
||||
|
||||
command -p -- ls
|
||||
@@ -0,0 +1,20 @@
|
||||
name: "Builtin Common Tests"
|
||||
cases:
|
||||
- name: "Piping builtin output"
|
||||
stdin: |
|
||||
shopt | wc -l | wc -l
|
||||
|
||||
- name: "Redirecting builtin output"
|
||||
stdin: |
|
||||
declare my_variable=10
|
||||
declare -p my_variable >out.txt
|
||||
|
||||
echo "Dumping file contents..."
|
||||
cat out.txt
|
||||
|
||||
- name: "Overrides"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare -p myvar
|
||||
myvar=10 declare -p myvar
|
||||
declare -p myvar
|
||||
@@ -0,0 +1,309 @@
|
||||
name: "Builtins: compgen"
|
||||
cases:
|
||||
- name: "compgen -A alias"
|
||||
stdin: |
|
||||
alias myalias="ls"
|
||||
alias myalias2="echo hi"
|
||||
|
||||
compgen -A alias myalias | sort
|
||||
|
||||
- name: "compgen -A binding"
|
||||
stdin: |
|
||||
compgen -A binding tab | sort
|
||||
|
||||
- name: "compgen -A builtin"
|
||||
stdin: |
|
||||
compgen -A builtin cd | sort
|
||||
|
||||
- name: "compgen -A directory"
|
||||
stdin: |
|
||||
touch somefile
|
||||
mkdir somedir
|
||||
mkdir somedir2
|
||||
|
||||
compgen -A directory some | sort
|
||||
|
||||
- name: "compgen -A file"
|
||||
stdin: |
|
||||
touch somefile
|
||||
mkdir somedir
|
||||
mkdir somedir2
|
||||
|
||||
compgen -A file some | sort
|
||||
|
||||
- name: "compgen -A file with dot files"
|
||||
stdin: |
|
||||
touch .file
|
||||
mkdir .dir
|
||||
|
||||
echo "[without dotglob]"
|
||||
shopt -u dotglob
|
||||
compgen -A file | sort
|
||||
|
||||
echo "[with dotglob]"
|
||||
shopt -s dotglob
|
||||
compgen -A file | sort
|
||||
|
||||
- name: "compgen -A function"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
echo hi
|
||||
}
|
||||
|
||||
myfunc2() {
|
||||
echo hello
|
||||
}
|
||||
|
||||
compgen -A function myfunc | sort
|
||||
|
||||
- name: "compgen -A keyword"
|
||||
stdin: |
|
||||
compgen -A keyword esa | sort
|
||||
|
||||
- name: "compgen -A variable"
|
||||
stdin: |
|
||||
declare myvar=10
|
||||
declare myvar2=11
|
||||
|
||||
compgen -A variable myvar | sort
|
||||
|
||||
- name: "compgen -W"
|
||||
stdin: |
|
||||
echo "1. Basic compgen -W"
|
||||
compgen -W "one two three" -- t | sort
|
||||
echo "2. compgen -W with expansion"
|
||||
myvar=value compgen -W '${myvar}'
|
||||
|
||||
- name: "compgen -W does not pathname-expand"
|
||||
stdin: |
|
||||
touch foo.txt bar.txt baz.log
|
||||
|
||||
echo "[glob pattern should be literal]"
|
||||
compgen -W '*.txt' | sort
|
||||
|
||||
echo "[question mark glob should be literal]"
|
||||
compgen -W '?.log' | sort
|
||||
|
||||
- name: "compgen -W with unsorted values"
|
||||
stdin: |
|
||||
compgen -W 'c b d a'
|
||||
|
||||
- name: "compgen -W with options"
|
||||
stdin: |
|
||||
compgen -W '--abc --def' -- '--ab'
|
||||
|
||||
- name: "compgen with no matches"
|
||||
stdin: |
|
||||
compgen -W yes no && echo "1. Result"
|
||||
|
||||
- name: "compgen -f with tilde"
|
||||
home_dir: testhome # relative to default working dir
|
||||
stdin: |
|
||||
mkdir testhome && cd testhome
|
||||
|
||||
echo "HOME base: $(basename $HOME)"
|
||||
echo "~ base:" $(basename ~)
|
||||
|
||||
touch item1
|
||||
|
||||
echo "[0]"
|
||||
for p in $(compgen -f ~); do
|
||||
echo ${p//$HOME/HOME}
|
||||
done
|
||||
|
||||
echo "[1]"
|
||||
for p in $(compgen -f ~/); do
|
||||
echo ${p//$HOME/HOME}
|
||||
done
|
||||
|
||||
- name: "compgen -f with quoted tilde"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
touch item1
|
||||
HOME=$(pwd)
|
||||
|
||||
echo "[0]"
|
||||
for p in $(compgen -f '~/'); do
|
||||
echo ${p//$HOME/HOME}
|
||||
done
|
||||
|
||||
- name: "compgen -f with quoted var"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
touch item1
|
||||
HOME=$(pwd)
|
||||
|
||||
echo "[0]"
|
||||
for p in $(compgen -f '$HOME/'); do
|
||||
echo ${p//$HOME/HOME}
|
||||
done
|
||||
|
||||
- name: "compgen -f with quoted + escaped var"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
touch item1
|
||||
HOME=$(pwd)
|
||||
|
||||
echo "[0]"
|
||||
for p in $(compgen -f '\$HOME/'); do
|
||||
echo ${p//$HOME/HOME}
|
||||
done
|
||||
|
||||
- name: "compgen with interesting hyphens"
|
||||
stdin: |
|
||||
compgen -P-before -S-after -W "one two three" -- t | sort
|
||||
|
||||
- name: "compgen -X"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
compgen -W 'foo bar' -X 'foo' ''
|
||||
|
||||
echo "[Take 2]"
|
||||
compgen -W 'foo bar' -X 'f*' ''
|
||||
|
||||
echo "[Take 3]"
|
||||
compgen -W '&1 foo' -X '\&*' ''
|
||||
|
||||
- name: "compgen -X with replacement"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
compgen -W 'somebody something' -X '&b*' some
|
||||
|
||||
- name: "compgen -X with extglob"
|
||||
stdin: |
|
||||
touch README
|
||||
shopt -s extglob
|
||||
|
||||
echo "[Take 1]"
|
||||
compgen -f READ
|
||||
|
||||
echo "[Take 2]"
|
||||
compgen -f -X "READ" READ
|
||||
|
||||
echo "[Take 3]"
|
||||
compgen -f -X "README" READ
|
||||
|
||||
echo "[Take 4]"
|
||||
compgen -f -X "!(READ)" READ
|
||||
|
||||
echo "[Take 5]"
|
||||
compgen -f -X "!(README)" READ
|
||||
|
||||
echo "[Take 6]"
|
||||
compgen -f -X "!!(READ)" READ
|
||||
|
||||
echo "[Take 7]"
|
||||
compgen -f -X "!!(README)" READ
|
||||
|
||||
- name: "compgen -o dirnames"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
compgen -W 'completion' -o dirnames -- c | sort
|
||||
|
||||
echo "[Take 2]"
|
||||
mkdir subdir
|
||||
touch subfile
|
||||
compgen -W 'completion' -o dirnames -- s | sort
|
||||
|
||||
- name: "compgen -o default"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
compgen -W 'completion' -o default -- c | sort
|
||||
|
||||
echo "[Take 2]"
|
||||
mkdir subdir
|
||||
touch subfile
|
||||
compgen -W 'completion' -o default -- s | sort
|
||||
|
||||
- name: "compgen -o bashdefault"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
compgen -W 'completion' -o bashdefault -- c | sort
|
||||
|
||||
echo "[Take 2]"
|
||||
mkdir subdir
|
||||
touch subfile
|
||||
compgen -W 'completion' -o bashdefault -- s | sort
|
||||
|
||||
- name: "compgen -o plusdirs"
|
||||
stdin: |
|
||||
echo "[Take 1]"
|
||||
touch cfile
|
||||
mkdir cdir
|
||||
compgen -W 'completion' -o plusdirs -o default -S suffix -- c | sort
|
||||
|
||||
- name: "compgen -A command"
|
||||
# Temporarily marked unconstrained Path case as `skip: true` to keep CI stable.
|
||||
# Environment-dependent due to host PATH contents(Ubuntu includes X11, while Arch and others do not).
|
||||
skip: true
|
||||
stdin: |
|
||||
compgen -A command | sort | grep -v "^brush"
|
||||
|
||||
- name: "compgen -A command in a constrained PATH"
|
||||
stdin: |
|
||||
SORT=$(command -v sort)
|
||||
GREP=$(command -v grep)
|
||||
PATH=$(pwd)
|
||||
compgen -A command | $SORT | $GREP -v "^brush"
|
||||
|
||||
- name: "compgen -A command builtins in a constrained PATH"
|
||||
stdin: |
|
||||
SORT=$(command -v sort)
|
||||
PATH=$(pwd)
|
||||
compgen -A command cd | $SORT
|
||||
|
||||
- name: "compgen -A command keywords in a constrained PATH"
|
||||
stdin: |
|
||||
SORT=$(command -v sort)
|
||||
PATH=$(pwd)
|
||||
compgen -A command [ | $SORT
|
||||
|
||||
- name: "compgen -A command function in a constrained PATH"
|
||||
stdin: |
|
||||
SORT=$(command -v sort)
|
||||
PATH=$(pwd)
|
||||
myfunc() {
|
||||
echo hi
|
||||
}
|
||||
myfunc2() {
|
||||
echo hello
|
||||
}
|
||||
compgen -A command myfunc | $SORT
|
||||
|
||||
- name: "compgen -A command with executable symlink"
|
||||
stdin: |
|
||||
# pwd
|
||||
# drwxr-xr-x ├── test-bin
|
||||
# .rwxr-xr-x │ ├── dummy_exe
|
||||
# .rw-r--r-- │ └── dummy_non_exe
|
||||
# drwxr-xr-x └── test-links
|
||||
# lrwxrwxrwx ├── link_valid ⇒ $(pwd)/test-bin/dummy_exe
|
||||
# lrwxrwxrwx └── link_valid_exe_rec ⇒ $(pwd)/test-links/link_valid
|
||||
# lrwxrwxrwx ├── link_non_exec ⇒ $(pwd)/test-bin/dummy_non_exe
|
||||
# lrwxrwxrwx ├── link_broken ⇒ $(pwd)/test-bin/dummy_non_exist
|
||||
#
|
||||
mkdir test-bin
|
||||
touch test-bin/dummy_exe
|
||||
touch test-bin/dummy_non_exe
|
||||
chmod +x test-bin/dummy_exe
|
||||
|
||||
mkdir test-links
|
||||
ln -s $(pwd)/test-bin/dummy_exe $(pwd)/test-links/link_valid
|
||||
ln -s $(pwd)/test-links/link_vaild $(pwd)/test-links/link_valid_rec
|
||||
ln -s $(pwd)/test-bin/dummy_non_exe $(pwd)/test-links/link_non_exec
|
||||
ln -s $(pwd)/test-bin/dummy_non_exist $(pwd)/test-links/link_broken
|
||||
|
||||
PATH="$(pwd)/test-links:$PATH"
|
||||
|
||||
echo "[Valid]"
|
||||
compgen -A command -- link_valid | sort
|
||||
|
||||
echo "[Invalid]"
|
||||
compgen -A command -- link_non_exec
|
||||
compgen -A command -- link_broken
|
||||
|
||||
- name: "compgen -F sets COMP_KEY and COMP_TYPE to 0"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
_comp() { echo "KEY=${COMP_KEY:-unset} TYPE=${COMP_TYPE:-unset}"; COMPREPLY=(); }
|
||||
compgen -F _comp mycmd
|
||||
@@ -0,0 +1,114 @@
|
||||
name: "Builtins: complete"
|
||||
cases:
|
||||
- name: "complete with no options"
|
||||
stdin: |
|
||||
complete -W foo mycmd
|
||||
complete
|
||||
|
||||
- name: "Roundtrip: complete -W"
|
||||
stdin: |
|
||||
complete -W foo mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -W 'foo bar' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -P"
|
||||
stdin: |
|
||||
complete -P myprefix mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -P 'my prefix' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -S"
|
||||
stdin: |
|
||||
complete -S mysuffix mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -S 'my suffix' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -F"
|
||||
stdin: |
|
||||
complete -Fmyfunc mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -F"
|
||||
stdin: |
|
||||
complete -G pattern mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -G 'pat tern' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -X"
|
||||
stdin: |
|
||||
complete -X pattern mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -X 'pat tern' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -C"
|
||||
stdin: |
|
||||
complete -C cmd mycmd
|
||||
complete -p mycmd
|
||||
|
||||
complete -C 'c md' mycmd
|
||||
complete -p mycmd
|
||||
|
||||
- name: "Roundtrip: complete -A"
|
||||
stdin: |
|
||||
for action in alias arrayvar binding builtin command directory disabled enabled export file 'function' group helptopic hostname job keyword running service setopt shopt signal stopped user variable; do
|
||||
complete -A ${action} mycmd
|
||||
complete -p mycmd
|
||||
done
|
||||
|
||||
- name: "Roundtrip: complete -o options"
|
||||
stdin: |
|
||||
for opt in bashdefault default dirnames filenames noquote nosort nospace plusdirs; do
|
||||
echo "--- Testing option: ${opt} ------------------"
|
||||
complete -o ${opt} mycmd_${opt}
|
||||
complete -p mycmd_${opt}
|
||||
done
|
||||
|
||||
- name: "complete -r"
|
||||
stdin: |
|
||||
echo "[Removing non-existent]"
|
||||
complete -r nonexistent
|
||||
echo $?
|
||||
|
||||
echo "[Removing existing]"
|
||||
complete -W token mycmd
|
||||
complete -r mycmd
|
||||
echo $?
|
||||
complete -p mycmd 2>/dev/null
|
||||
|
||||
- name: "complete -r with no args"
|
||||
stdin: |
|
||||
complete -W token mycmd1
|
||||
complete -W token mycmd2
|
||||
|
||||
complete -r
|
||||
echo $?
|
||||
|
||||
complete -p
|
||||
|
||||
- name: "complete -r with special options"
|
||||
stdin: |
|
||||
complete -W token mycmd
|
||||
complete -W other -E
|
||||
|
||||
complete -r -E
|
||||
echo $?
|
||||
|
||||
complete -p
|
||||
|
||||
- name: "compgen -W preserves duplicates"
|
||||
stdin: |
|
||||
compgen -W "a b a c b"
|
||||
|
||||
- name: "compgen -W preserves order"
|
||||
stdin: |
|
||||
compgen -W "z y x a"
|
||||
@@ -0,0 +1,451 @@
|
||||
name: "Builtins: declare"
|
||||
common_test_files:
|
||||
- path: "helpers.sh"
|
||||
contents: |
|
||||
stable_print_assoc_array() {
|
||||
# TODO(nameref): enable use of nameref when implemented; for now
|
||||
# we assume the name of the array is assoc_array
|
||||
# local -n assoc_array=$1
|
||||
local key
|
||||
|
||||
for key in $(printf "%s\n" "${!assoc_array[@]}" | sort -n); do
|
||||
echo "\"${key}\" => ${assoc_array[${key}]}"
|
||||
done
|
||||
}
|
||||
|
||||
cases:
|
||||
- name: "Display vars"
|
||||
stdin: |
|
||||
declare myvar=something
|
||||
declare -p myvar
|
||||
|
||||
myarr=(a b c)
|
||||
declare -p myarr
|
||||
|
||||
- name: "Display vars with interesting chars"
|
||||
stdin: |
|
||||
(testvar="\"abc\"" && declare -p testvar && declare | grep testvar=)
|
||||
echo "-------------------------"
|
||||
(testvar="a b c" && declare -p testvar && declare | grep testvar=)
|
||||
echo "-------------------------"
|
||||
(testvar="'" && declare -p testvar && declare | grep testvar=)
|
||||
|
||||
- name: "Display vars with interesting chars 2"
|
||||
min_oracle_version: 5.2 # some sequences render differently in older shell versions
|
||||
stdin: |
|
||||
(testvar=$'a\nb' && declare -p testvar && declare | grep testvar=)
|
||||
echo "-------------------------"
|
||||
(testvar=$'\x03' && declare -p testvar && declare | grep testvar=)
|
||||
echo "-------------------------"
|
||||
(testvar=$'\x08' && declare -p testvar && declare | grep testvar=)
|
||||
echo "-------------------------"
|
||||
(testvar=$(printf '\033[34mabc\033[0m') && declare -p testvar && declare | grep testvar=)
|
||||
|
||||
- name: "Declare integer"
|
||||
stdin: |
|
||||
declare -i num=10
|
||||
declare -p num
|
||||
|
||||
echo $num
|
||||
num+=10
|
||||
echo $num
|
||||
|
||||
declare +i num
|
||||
declare -p num
|
||||
|
||||
echo $num
|
||||
num+=10
|
||||
echo $num
|
||||
|
||||
- name: "Declare integer with non-integer string"
|
||||
stdin: |
|
||||
declare -i var=value
|
||||
declare -p var
|
||||
|
||||
- name: "Update integer with non-integer string"
|
||||
stdin: |
|
||||
declare -i var=10
|
||||
declare -p var
|
||||
|
||||
var=value
|
||||
declare -p var
|
||||
|
||||
- name: "Update integer array with non-integer string"
|
||||
stdin: |
|
||||
declare -ai arr=()
|
||||
declare -p arr
|
||||
|
||||
arr[0]="value"
|
||||
declare -p arr
|
||||
|
||||
- name: "Update integer array with non-integer string"
|
||||
stdin: |
|
||||
declare -Ai arr=()
|
||||
declare -p arr
|
||||
|
||||
arr['key']="value"
|
||||
declare -p arr
|
||||
|
||||
- name: "Declare readonly variable"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare -r var="readonly"
|
||||
declare -p var
|
||||
|
||||
echo $var
|
||||
|
||||
var="change"
|
||||
echo "change result: $?"
|
||||
echo "var: ${var}"
|
||||
|
||||
declare +r var
|
||||
echo "+r result: $?"
|
||||
declare -p var
|
||||
|
||||
- name: "Declare array"
|
||||
stdin: |
|
||||
declare -a arr=("element1" "element2" "element3")
|
||||
declare -p arr
|
||||
echo "[0]: ${arr[0]}"
|
||||
echo "[1]: ${arr[0]}"
|
||||
echo "[2]: ${arr[0]}"
|
||||
echo "[3]: ${arr[0]}"
|
||||
echo "STAR: ${arr[*]}"
|
||||
echo "AT: ${arr[*]}"
|
||||
|
||||
- name: "Declare associative array"
|
||||
stdin: |
|
||||
declare -A arr=(["x1"]=1 ["x2"]=2)
|
||||
declare -p arr
|
||||
echo "[x]: ${arr[x1]}"
|
||||
echo "[y]: ${arr[x2]}"
|
||||
echo "[z]: ${arr[x3]}"
|
||||
echo "STAR: ${arr[*]}"
|
||||
echo "AT: ${arr[*]}"
|
||||
|
||||
- name: "Declare and export variable"
|
||||
stdin: |
|
||||
declare -x myexportedvar="exported variable"
|
||||
env | grep myexportedvar
|
||||
|
||||
- name: "Declare and export unset variable"
|
||||
stdin: |
|
||||
declare -x myunsetexportedvar
|
||||
env | grep myunsetexportedvar
|
||||
set | grep myunsetexportedvar=
|
||||
|
||||
- name: "Re-declaring variable"
|
||||
stdin: |
|
||||
var="value"
|
||||
declare var
|
||||
echo "var: ${var}"
|
||||
|
||||
- name: "Declaring without value"
|
||||
stdin: |
|
||||
[[ -v var ]] && echo "1: Variable is set"
|
||||
declare var
|
||||
declare -p var
|
||||
[[ -v var ]] && echo "2: Variable is set"
|
||||
declare var2=""
|
||||
declare -p var2
|
||||
[[ -v var2 ]] && echo "3: Variable is set"
|
||||
|
||||
- name: "Displaying local vars"
|
||||
stdin: |
|
||||
function test {
|
||||
echo "Dumping local variables (should be empty)"
|
||||
local -p
|
||||
|
||||
local -i int_var=10
|
||||
local -A assoc_array=(["x1"]=1 ["x2"]=2)
|
||||
local -a array=(a b c)
|
||||
local -r ro_var="readonly"
|
||||
local -t traced="value"
|
||||
|
||||
echo "Dump all variables"
|
||||
local -p
|
||||
}
|
||||
|
||||
test
|
||||
|
||||
- name: "Using local to detect function presence"
|
||||
stdin: |
|
||||
function test {
|
||||
local something 2>/dev/null && echo "In function"
|
||||
}
|
||||
|
||||
local something 2>/dev/null || echo "Not in function"
|
||||
|
||||
test
|
||||
|
||||
- name: "Displaying function names"
|
||||
stdin: |
|
||||
echo "Dumping function names"
|
||||
declare -F
|
||||
declare -p -F
|
||||
|
||||
function test {
|
||||
:
|
||||
}
|
||||
|
||||
echo "Dumping function names again"
|
||||
declare -F
|
||||
declare -p -F
|
||||
|
||||
echo "Dumping test"
|
||||
declare -F test
|
||||
declare -p -F test
|
||||
|
||||
- name: "Displaying functions"
|
||||
stdin: |
|
||||
echo "Dumping functions"
|
||||
declare -f
|
||||
declare -p -f
|
||||
|
||||
function test {
|
||||
:
|
||||
}
|
||||
|
||||
echo "Dumping functions again"
|
||||
declare -f
|
||||
declare -p -f
|
||||
|
||||
echo "Dumping test"
|
||||
declare -f test
|
||||
declare -p -f test
|
||||
|
||||
- name: "Displaying non-existent functions"
|
||||
stdin: |
|
||||
declare -f not_a_function
|
||||
echo "Result (-f): $?"
|
||||
|
||||
declare -F not_a_function
|
||||
echo "Result (-F): $?"
|
||||
|
||||
- name: "Valid conversions"
|
||||
stdin: |
|
||||
declare -a arr1=(a b c)
|
||||
declare -a arr1
|
||||
echo "Conversion result: $?"
|
||||
declare -p arr1
|
||||
|
||||
declare -A arr2=(["x1"]=1 ["x2"]=2)
|
||||
declare -A arr2
|
||||
echo "Conversion result: $?"
|
||||
declare -p arr2
|
||||
|
||||
declare scalar1="value"
|
||||
declare -a scalar1
|
||||
echo "Conversion result: $?"
|
||||
declare -p scalar1
|
||||
|
||||
declare scalar2="value"
|
||||
declare -A scalar2
|
||||
echo "Conversion result: $?"
|
||||
declare -p scalar2
|
||||
|
||||
- name: "Bad conversions"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare -a arr1=(a b c)
|
||||
declare -A arr1
|
||||
echo "Conversion result: $?"
|
||||
declare -p arr1
|
||||
|
||||
declare -A arr2=(["x1"]=1 ["x2"]=2)
|
||||
declare -a arr2
|
||||
echo "Conversion result: $?"
|
||||
declare -p arr2
|
||||
|
||||
- name: "Declare -p using invalid forms"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare arr=(a b c)
|
||||
declare -p arr[0]
|
||||
echo "Result: $?"
|
||||
declare -p arr[0]=1
|
||||
echo "Result: $?"
|
||||
|
||||
declare scalar=x
|
||||
echo "Result: $?"
|
||||
declare -p scalar=y
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "Updating value"
|
||||
stdin: |
|
||||
declare var="value"
|
||||
declare -p var
|
||||
declare var="changed"
|
||||
declare -p var
|
||||
|
||||
- name: "Updating value attributes"
|
||||
stdin: |
|
||||
declare -ix var=10
|
||||
declare -p var
|
||||
declare +ix var
|
||||
declare -p var
|
||||
|
||||
- name: "Updating array"
|
||||
stdin: |
|
||||
declare arr=(a b c)
|
||||
declare -p arr
|
||||
declare arr=(d e)
|
||||
declare -p arr
|
||||
declare arr=10
|
||||
declare -p arr
|
||||
|
||||
declare arr2=a
|
||||
declare -p arr2
|
||||
declare -A arr2=(["key"]="value")
|
||||
declare -p arr2
|
||||
|
||||
- name: "Updating causing conversion"
|
||||
stdin: |
|
||||
source helpers.sh
|
||||
|
||||
declare assoc_array="scalar-value"
|
||||
declare -p assoc_array
|
||||
|
||||
declare -A assoc_array["key"]="key-value"
|
||||
stable_print_assoc_array assoc_array
|
||||
|
||||
- name: "Uppercase attribute"
|
||||
stdin: |
|
||||
declare var=value
|
||||
|
||||
declare -u var
|
||||
declare -p var
|
||||
|
||||
var="abcd"
|
||||
declare -p var
|
||||
|
||||
declare another="abcd"
|
||||
declare -u another
|
||||
another+=efg
|
||||
declare -p another
|
||||
|
||||
- name: "Lowercase attribute"
|
||||
stdin: |
|
||||
declare var=value
|
||||
|
||||
declare -l var
|
||||
declare -p var
|
||||
|
||||
var="AbCd"
|
||||
declare -p var
|
||||
|
||||
declare another="ABCD"
|
||||
declare -l another
|
||||
another+=EFG
|
||||
declare -p another
|
||||
|
||||
- name: "Capitalize attribute"
|
||||
stdin: |
|
||||
declare var=value
|
||||
|
||||
declare -c var
|
||||
declare -p var
|
||||
|
||||
var="aBcD eFg"
|
||||
declare -p var
|
||||
|
||||
declare another="aBcD"
|
||||
declare -c another
|
||||
another+=eFg
|
||||
declare -p another
|
||||
|
||||
- name: "Declare invalid identifiers"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare 1=x && echo "Set 1"
|
||||
declare @=10 && echo "Set @"
|
||||
|
||||
- name: "Nameref declare -p shows nameref attribute"
|
||||
stdin: |
|
||||
target="hello"
|
||||
declare -n ref=target
|
||||
declare -p ref
|
||||
declare -p target
|
||||
|
||||
- name: "Nameref declare -n with initial value"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target="original"
|
||||
declare -n ref=target
|
||||
echo "ref: $ref"
|
||||
echo "target: $target"
|
||||
|
||||
- name: "Nameref remove attribute with +n"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target="value"
|
||||
declare -n ref=target
|
||||
echo "as nameref: $ref"
|
||||
declare +n ref
|
||||
echo "after +n: ref=$ref"
|
||||
declare -p ref
|
||||
|
||||
- name: "Nameref to array via declare"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
arr=(one two three)
|
||||
declare -n ref=arr
|
||||
echo "ref[0]: ${ref[0]}"
|
||||
echo "ref[1]: ${ref[1]}"
|
||||
echo "ref[@]: ${ref[@]}"
|
||||
echo "ref[*]: ${ref[*]}"
|
||||
|
||||
- name: "Nameref array modification via declare"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
arr=(a b c)
|
||||
declare -n ref=arr
|
||||
ref[1]="modified"
|
||||
echo "arr[1]: ${arr[1]}"
|
||||
echo "ref[1]: ${ref[1]}"
|
||||
|
||||
- name: "Nameref to associative array via declare"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -A assoc=(["key1"]="val1" ["key2"]="val2")
|
||||
declare -n ref=assoc
|
||||
echo "ref[key1]: ${ref[key1]}"
|
||||
echo "ref[key2]: ${ref[key2]}"
|
||||
|
||||
- name: "Nameref array iteration in function via local -n"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
print_array() {
|
||||
local -n arr=$1
|
||||
echo "count: ${#arr[@]}"
|
||||
for elem in "${arr[@]}"; do
|
||||
echo "elem: $elem"
|
||||
done
|
||||
}
|
||||
|
||||
myarr=(first second third)
|
||||
print_array myarr
|
||||
|
||||
- name: "Nameref get keys via declare"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -A assoc=(["a"]=1 ["b"]=2 ["c"]=3)
|
||||
declare -n ref=assoc
|
||||
echo "keys (sorted): $(printf '%s\n' ${!ref[@]} | sort | tr '\n' ' ')"
|
||||
echo "count: ${#ref[@]}"
|
||||
|
||||
- name: "Nameref array length via declare"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
arr=(one two three four five)
|
||||
declare -n ref=arr
|
||||
echo "length: ${#ref[@]}"
|
||||
echo "first element length: ${#ref[0]}"
|
||||
|
||||
- name: "Nameref declare -n filter"
|
||||
stdin: |
|
||||
regular="plain"
|
||||
declare -n ref1=regular
|
||||
declare -n ref2=regular
|
||||
not_a_ref="also plain"
|
||||
declare -p -n 2>/dev/null | sort
|
||||
@@ -0,0 +1,135 @@
|
||||
name: "Builtins: dot"
|
||||
cases:
|
||||
- name: "Basic dot usage"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In sourced script"
|
||||
var="updated"
|
||||
stdin: |
|
||||
var="orig"
|
||||
. script.sh
|
||||
echo "var: ${var}"
|
||||
|
||||
- name: "Basic source usage"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In sourced script"
|
||||
for f in ${FUNCNAME[@]}; do
|
||||
echo "FUNCNAME: '${f}'"
|
||||
done
|
||||
for s in ${BASH_SOURCE[@]}; do
|
||||
echo "BASH_SOURCE: '${s}'"
|
||||
done
|
||||
var="updated"
|
||||
|
||||
function script_func() {
|
||||
echo "In script func"
|
||||
for f in ${FUNCNAME[@]}; do
|
||||
echo "FUNCNAME: '${f}'"
|
||||
done
|
||||
for s in ${BASH_SOURCE[@]}; do
|
||||
echo "BASH_SOURCE: '${s}'"
|
||||
done
|
||||
}
|
||||
stdin: |
|
||||
var="orig"
|
||||
source ./script.sh
|
||||
echo "var: ${var}"
|
||||
script_func
|
||||
|
||||
- name: "Source non-existent file path"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
source script.sh
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "Source dir"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
source .
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "Source script without args"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In sourced script"
|
||||
echo "Args: $@"
|
||||
stdin: |
|
||||
set -- outer1 outer2 outer3
|
||||
source script.sh
|
||||
|
||||
- name: "Source script with args"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In sourced script"
|
||||
echo "Args: $@"
|
||||
stdin: |
|
||||
set -- outer1 outer2 outer3
|
||||
source script.sh inner1 inner2
|
||||
|
||||
- name: "Source with redirection"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In sourced script"
|
||||
stdin: |
|
||||
source script.sh arg1 arg2 > out.txt
|
||||
echo "Sourced script; dumping..."
|
||||
cat out.txt
|
||||
|
||||
- name: "Source with return"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "Before return"
|
||||
return 42
|
||||
echo "After return"
|
||||
stdin: |
|
||||
echo "Before source"
|
||||
source script.sh
|
||||
echo "After source, exit code: $?"
|
||||
|
||||
- name: "Source with exit"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "Before exit"
|
||||
exit 42
|
||||
echo "After exit"
|
||||
stdin: |
|
||||
echo "Before source"
|
||||
source script.sh
|
||||
echo "After source"
|
||||
|
||||
- name: "Source with break in caller's loop"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In script, breaking"
|
||||
break
|
||||
stdin: |
|
||||
for i in 1 2 3; do
|
||||
echo "Iteration $i"
|
||||
source script.sh
|
||||
echo "After source"
|
||||
done
|
||||
echo "After loop"
|
||||
|
||||
- name: "Source with continue in caller's loop"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
echo "In script, continuing"
|
||||
continue
|
||||
echo "After continue"
|
||||
stdin: |
|
||||
for i in 1 2 3; do
|
||||
echo "Iteration $i"
|
||||
source script.sh
|
||||
echo "After source"
|
||||
done
|
||||
echo "After loop"
|
||||
@@ -0,0 +1,12 @@
|
||||
name: "Builtins: echo"
|
||||
cases:
|
||||
- name: "echo with only --"
|
||||
stdin: echo --
|
||||
|
||||
- name: "echo with -- and args"
|
||||
stdin: echo -- -1 --"aaa" ?^1as-
|
||||
|
||||
- name: "echo -e"
|
||||
stdin: |
|
||||
echo -e "1. \x65" | hexdump -C
|
||||
echo -e "2. \x{65}" | hexdump -C
|
||||
@@ -0,0 +1,32 @@
|
||||
name: "Builtins: enable"
|
||||
cases:
|
||||
- name: "List special builtins"
|
||||
stdin: enable -s
|
||||
|
||||
- name: "List default-disabled builtins"
|
||||
stdin: enable -n
|
||||
|
||||
- name: "List all builtins"
|
||||
stdin: |
|
||||
# List builtins but ignore any brush specific ones.
|
||||
enable | grep -v brush
|
||||
|
||||
- name: "Disable builtins"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
type printf
|
||||
|
||||
# Disable the builtin
|
||||
PATH=
|
||||
enable -n printf
|
||||
|
||||
# Check
|
||||
type printf
|
||||
print "Gone\n"
|
||||
|
||||
# Re-enable
|
||||
enable printf
|
||||
|
||||
# Re-check
|
||||
type printf
|
||||
printf "Back\n"
|
||||
@@ -0,0 +1,47 @@
|
||||
name: "Builtins: eval"
|
||||
cases:
|
||||
- name: "Basic eval usage"
|
||||
stdin: |
|
||||
eval 'echo 1 + 1 == $((1 + 1))'
|
||||
|
||||
- name: "eval with return in function"
|
||||
stdin: |
|
||||
f() {
|
||||
eval 'return 42'
|
||||
echo "Should not reach here"
|
||||
}
|
||||
f
|
||||
echo "Exit code: $?"
|
||||
|
||||
- name: "eval with exit"
|
||||
stdin: |
|
||||
eval 'exit 42'
|
||||
echo "After eval'd exit"
|
||||
|
||||
- name: "eval with break in loop"
|
||||
stdin: |
|
||||
for i in 1 2 3; do
|
||||
eval 'break'
|
||||
echo "After eval'd break"
|
||||
done
|
||||
echo "After loop"
|
||||
|
||||
- name: "eval with continue in loop"
|
||||
stdin: |
|
||||
for i in 1 2 3; do
|
||||
echo "Iteration $i"
|
||||
eval 'continue'
|
||||
echo "After eval'd continue"
|
||||
done
|
||||
echo "After loop"
|
||||
|
||||
- name: "eval with nested break levels"
|
||||
stdin: |
|
||||
for i in 1 2; do
|
||||
for j in a b c; do
|
||||
eval 'break 2'
|
||||
echo "After eval'd break (inner loop)"
|
||||
done
|
||||
echo "After eval'd break (outer loop)"
|
||||
done
|
||||
echo "After outer loop"
|
||||
@@ -0,0 +1,51 @@
|
||||
name: "Builtins: exec"
|
||||
cases:
|
||||
- name: "Exec an invalid path"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
exec /some/nonexistent/path
|
||||
|
||||
- name: "Exec with no arguments"
|
||||
stdin: |
|
||||
pid=$$
|
||||
exec
|
||||
[[ "${pid}" == "$$" ]] || echo "PID changed"
|
||||
|
||||
- name: "Exec with no arguments and redirection for new fd"
|
||||
stdin: |
|
||||
exec 3>some-file.txt
|
||||
echo "Hello, fd 3!" >&3
|
||||
|
||||
- name: "Exec with no arguments and redirection of stderr"
|
||||
stdin: |
|
||||
exec 2>/dev/null
|
||||
|
||||
# Do something that should display an error to stderr
|
||||
source /non-existent
|
||||
|
||||
- name: "Exec with no arguments and redirection in a script"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
exec 2>output-file.txt
|
||||
echo hi >&2
|
||||
args: ["./script.sh"]
|
||||
|
||||
- name: "Exec a command"
|
||||
stdin: |
|
||||
exec $0 -c 'echo "In child shell"'
|
||||
echo "This is never reached"
|
||||
|
||||
- name: "exec without -c"
|
||||
stdin: |
|
||||
export myvar=value
|
||||
exec $0 -c 'echo "myvar: ${myvar}"'
|
||||
|
||||
- name: "exec -c"
|
||||
stdin: |
|
||||
export myvar=value
|
||||
exec -c $0 -c 'echo "myvar: ${myvar}"'
|
||||
|
||||
- name: "exec -a"
|
||||
stdin: |
|
||||
exec -a shellname $0 -c 'echo "0: $0"'
|
||||
@@ -0,0 +1,85 @@
|
||||
name: "Builtins: exit"
|
||||
cases:
|
||||
- name: "Exit without code"
|
||||
stdin: |
|
||||
exit
|
||||
|
||||
- name: "Exit with code"
|
||||
stdin: |
|
||||
exit 10
|
||||
|
||||
- name: "Exit with i64 max"
|
||||
stdin: |
|
||||
exit 9223372036854775807
|
||||
|
||||
- name: "Exit with i64 min"
|
||||
stdin: |
|
||||
exit -9223372036854775808
|
||||
|
||||
- name: "Exit in for loop"
|
||||
stdin: |
|
||||
for i in 1 2 3; do
|
||||
exit 42
|
||||
echo "Got past exit in loop"
|
||||
done
|
||||
echo "Got past loop"
|
||||
|
||||
- name: "Exit in arithmetic for loop body"
|
||||
stdin: |
|
||||
for ((i = 0; i < 10; i++)); do
|
||||
exit 42
|
||||
echo "Got past exit in loop"
|
||||
done
|
||||
echo "Got past loop"
|
||||
|
||||
- name: "Exit in while loop condition"
|
||||
stdin: |
|
||||
while exit 42; do
|
||||
echo "In loop"
|
||||
done
|
||||
echo "Got past loop"
|
||||
|
||||
- name: "Exit in while loop body"
|
||||
stdin: |
|
||||
while true; do
|
||||
exit 42
|
||||
echo "Got past exit in body"
|
||||
break
|
||||
done
|
||||
echo "Got past loop"
|
||||
|
||||
- name: "Exit in sequence"
|
||||
stdin: |
|
||||
exit 42; echo "Should not be printed"
|
||||
|
||||
- name: "Exit in function"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
exit 42
|
||||
echo "Got past exit in function"
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Got past function call"
|
||||
|
||||
- name: "Exit in subshell"
|
||||
stdin: |
|
||||
(exit 42)
|
||||
echo "Got past subshell"
|
||||
|
||||
- name: "Exit in command substitution"
|
||||
stdin: |
|
||||
output=$(echo hi; exit 42; echo there)
|
||||
echo "Got past command substitution"
|
||||
|
||||
- name: "Exit in and/or"
|
||||
stdin: |
|
||||
exit 42 || echo "Got past exit"
|
||||
|
||||
- name: "Exit in brace group"
|
||||
stdin: |
|
||||
{
|
||||
exit 42
|
||||
echo "Got past exit"
|
||||
}
|
||||
echo "Got past brace group"
|
||||
@@ -0,0 +1,36 @@
|
||||
name: "Builtins: export"
|
||||
cases:
|
||||
- name: "Basic export usage"
|
||||
stdin: |
|
||||
MY_TEST_VAR="value"
|
||||
echo "Looking for unexported variable..."
|
||||
env | grep MY_TEST_VAR
|
||||
|
||||
echo "Exporting and looking for it again..."
|
||||
export MY_TEST_VAR
|
||||
env | grep MY_TEST_VAR
|
||||
|
||||
echo "Exporting with new value..."
|
||||
export MY_TEST_VAR="changed value"
|
||||
env | grep MY_TEST_VAR
|
||||
|
||||
- name: "Exporting array"
|
||||
stdin: |
|
||||
export arr=(a 1 2)
|
||||
declare -p arr
|
||||
|
||||
- name: "Unexporting variable"
|
||||
stdin: |
|
||||
MY_TEST_VAR="value"
|
||||
export MY_TEST_VAR="value"
|
||||
export -n MY_TEST_VAR
|
||||
|
||||
env | grep MY_TEST_VAR
|
||||
|
||||
- name: "Export with assignment through nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=MY_EXPORT_VAR
|
||||
export ref="exported_value"
|
||||
echo "MY_EXPORT_VAR: $MY_EXPORT_VAR"
|
||||
env | grep MY_EXPORT_VAR
|
||||
@@ -0,0 +1,275 @@
|
||||
name: "Builtins: fc"
|
||||
cases:
|
||||
- name: "fc -l lists recent commands"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -l
|
||||
|
||||
- name: "fc -l with range (positive indices)"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
: cmd4
|
||||
: cmd5
|
||||
fc -l 2 4
|
||||
|
||||
- name: "fc -l with single argument"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -l 2
|
||||
|
||||
- name: "fc -l with negative indices"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
: cmd4
|
||||
: cmd5
|
||||
fc -l -3 -1
|
||||
|
||||
- name: "fc -l with string search"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
: cmd1
|
||||
: other
|
||||
: cmd2
|
||||
: something
|
||||
fc -l cmd
|
||||
|
||||
- name: "fc -l with -n (no line numbers)"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -ln
|
||||
|
||||
- name: "fc -l with -r (reverse order)"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -lr
|
||||
|
||||
- name: "fc -l with -n and -r combined"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -lnr
|
||||
|
||||
- name: "fc -l with range and -r"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
: cmd4
|
||||
: cmd5
|
||||
fc -lr 2 4
|
||||
|
||||
- name: "fc -l 0 means -1"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
fc -l 0
|
||||
|
||||
- name: "fc -l defaults to last 16 commands"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
: cmd4
|
||||
: cmd5
|
||||
: cmd6
|
||||
: cmd7
|
||||
: cmd8
|
||||
: cmd9
|
||||
: cmd10
|
||||
: cmd11
|
||||
: cmd12
|
||||
: cmd13
|
||||
: cmd14
|
||||
: cmd15
|
||||
: cmd16
|
||||
: cmd17
|
||||
: cmd18
|
||||
fc -l
|
||||
|
||||
- name: "fc -s re-executes last command"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo hello
|
||||
fc -s
|
||||
|
||||
- name: "fc -s with command argument"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo hello
|
||||
echo world
|
||||
fc -s echo
|
||||
|
||||
- name: "fc -s with pattern replacement"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo hello
|
||||
fc -s hello=goodbye
|
||||
|
||||
- name: "fc -s with command number"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo first
|
||||
echo second
|
||||
echo third
|
||||
fc -s 1
|
||||
|
||||
- name: "fc -s with negative offset"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo first
|
||||
echo second
|
||||
echo third
|
||||
fc -s -2
|
||||
|
||||
- name: "fc -s with string prefix match"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo aaa
|
||||
echo bbb
|
||||
echo aac
|
||||
fc -s aa
|
||||
|
||||
- name: "fc -s with pattern replacement and command number"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo first
|
||||
echo second
|
||||
echo hello
|
||||
fc -s hello=goodbye 3
|
||||
|
||||
- name: "fc -s with pattern replacement and string prefix"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo aaa
|
||||
: somecommand
|
||||
echo hello world
|
||||
fc -s hello=goodbye echo
|
||||
|
||||
- name: "fc -s with pattern replacement and negative index"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo one
|
||||
echo two
|
||||
echo hello world
|
||||
fc -s hello=goodbye -1
|
||||
|
||||
- name: "fc -s pattern replacement when pattern doesn't match"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo hello
|
||||
fc -s xyz=abc
|
||||
|
||||
- name: "fc -s pattern replacement replaces all occurrences"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo test test test
|
||||
fc -s test=foo
|
||||
|
||||
- name: "fc -s with empty replacement"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo hello world
|
||||
fc -s hello=
|
||||
|
||||
- name: "fc with invalid range"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
fc -l 100 200
|
||||
expect_status: 1
|
||||
|
||||
- name: "fc -s with nonexistent command"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo hello
|
||||
fc -s xyz
|
||||
expect_status: 1
|
||||
|
||||
- name: "fc -l with both string and number"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd3
|
||||
: other
|
||||
fc -l cmd 3
|
||||
@@ -0,0 +1,476 @@
|
||||
name: "Builtins: getopts"
|
||||
cases:
|
||||
- name: "Basic getopts"
|
||||
stdin: |
|
||||
func() {
|
||||
echo "Beginning of args"
|
||||
while getopts "ab:" option "$@"; do
|
||||
case ${option} in
|
||||
a) echo "Option a; OPTARG=${OPTARG}";;
|
||||
b) echo "Option b; OPTARG=${OPTARG}";;
|
||||
*) echo "Unknown option: ${option}";;
|
||||
esac
|
||||
echo "OPTIND is now ${OPTIND}"
|
||||
echo "OPTERR is now ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Result: $?"
|
||||
echo "option: ${option}"
|
||||
echo "End of args"
|
||||
}
|
||||
|
||||
func -a -b my_b_arg
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: no args"
|
||||
stdin: |
|
||||
getopts "a:b:" myvar
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: toplevel positional parameters"
|
||||
stdin: |
|
||||
set -- -a value
|
||||
getopts "a:b:" myvar
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: function positional parameters"
|
||||
stdin: |
|
||||
my_function() {
|
||||
getopts "a:b:" myvar
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
}
|
||||
|
||||
my_function -b value
|
||||
|
||||
- name: "getopts: function script parameters"
|
||||
test_files:
|
||||
- path: script.sh
|
||||
contents: |
|
||||
getopts "a:b:" myvar
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
stdin: |
|
||||
source ./script.sh -a value
|
||||
|
||||
- name: "getopts: no options given"
|
||||
stdin: |
|
||||
while getopts "a:b:" myvar notoption; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: options and non-options"
|
||||
stdin: |
|
||||
while getopts "ab:" myvar -a -b value notoption; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: --"
|
||||
stdin: |
|
||||
while getopts "ab:" myvar -a -b value -- -c notoption; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: -- as first arg"
|
||||
stdin: |
|
||||
while getopts "ab:" myvar -- -a; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: invalid option when optstr starts with colon"
|
||||
stdin: |
|
||||
getopts ":a:" myvar -b value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: invalid option when optstr does not start with colon"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
getopts "a:" myvar -b value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: multiple options in one token"
|
||||
stdin: |
|
||||
while getopts "a:bcdefg" myvar -fedcba something -g; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
- name: "getopts: short option and value in single arg"
|
||||
stdin: |
|
||||
getopts "p:" myvar -p-
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
|
||||
while getopts "abep:" myvar -abpsomething-else; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "OPTERR: ${OPTERR}"
|
||||
echo "-----------------"
|
||||
done
|
||||
|
||||
- name: "getopts: OPTERR=0 suppresses error for unknown option"
|
||||
stdin: |
|
||||
OPTERR=0
|
||||
getopts "a:" myvar -x 2>/dev/null
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTERR=1 shows error for unknown option"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
OPTERR=1
|
||||
getopts "a:" myvar -x
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTERR nonzero shows error"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
OPTERR=42
|
||||
getopts "a:" myvar -x
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
|
||||
- name: "getopts: missing required argument without leading colon"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
getopts "a:" myvar -a
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: missing required argument with leading colon"
|
||||
stdin: |
|
||||
getopts ":a:" myvar -a
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: missing required argument with OPTERR=0"
|
||||
stdin: |
|
||||
OPTERR=0
|
||||
getopts "a:" myvar -a 2>/dev/null
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: combined unknown and missing arg errors"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
func() {
|
||||
while getopts "a:b:" opt "$@"; do
|
||||
case "$opt" in
|
||||
a) echo "a=$OPTARG" ;;
|
||||
b) echo "b=$OPTARG" ;;
|
||||
\?) echo "unknown" ;;
|
||||
:) echo "missing arg for $OPTARG" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
func -a foo -x -b
|
||||
echo "Done: $?"
|
||||
|
||||
- name: "getopts: bare dash as argument"
|
||||
stdin: |
|
||||
getopts "ab:" myvar -
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: leading colon with dash-colon option"
|
||||
stdin: |
|
||||
getopts ":a:" myvar -:
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: unknown option in middle of combined flags"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
while getopts "ac" myvar -axc; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
|
||||
- name: "getopts: missing arg in combined flags"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
while getopts "a:b" myvar -ba; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Final: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: combined flags with arg-taking option consumes next arg"
|
||||
stdin: |
|
||||
while getopts "a:b" myvar -ba nextarg; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTIND reset between calls"
|
||||
stdin: |
|
||||
while getopts "a:b" myvar -b -a foo; do
|
||||
echo "myvar: ${myvar}; OPTARG: ${OPTARG-UNSET}; OPTIND: ${OPTIND}"
|
||||
done
|
||||
echo "--- reset ---"
|
||||
OPTIND=1
|
||||
while getopts "a:b" myvar -b -a bar; do
|
||||
echo "myvar: ${myvar}; OPTARG: ${OPTARG-UNSET}; OPTIND: ${OPTIND}"
|
||||
done
|
||||
|
||||
- name: "getopts: empty optstring"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
getopts "" myvar -a
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTIND=0"
|
||||
stdin: |
|
||||
OPTIND=0
|
||||
getopts "a:" myvar -a value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: negative OPTIND treated as reset"
|
||||
stdin: |
|
||||
OPTIND=-1
|
||||
getopts "a:" myvar -a value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: bare dash among other args"
|
||||
stdin: |
|
||||
while getopts "ab" myvar -a - -b; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTIND past end of args"
|
||||
stdin: |
|
||||
OPTIND=100
|
||||
getopts "a:b:" myvar -a value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: missing arg with leading colon in combined flags"
|
||||
stdin: |
|
||||
while getopts ":a:b" myvar -ba; do
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
echo "----------------"
|
||||
done
|
||||
echo "Done; result: $?"
|
||||
|
||||
- name: "getopts: non-numeric OPTIND treated as reset"
|
||||
stdin: |
|
||||
OPTIND=abc
|
||||
getopts "a:" myvar -a value
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: option value is empty string"
|
||||
stdin: |
|
||||
getopts "a:" myvar -a ""
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: '${OPTARG}'"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: option value looks like another option"
|
||||
stdin: |
|
||||
getopts "a:b" myvar -a -b
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: OPTIND=0 resets mid-parse of combined options"
|
||||
stdin: |
|
||||
getopts "abc" myvar -abc
|
||||
echo "first: ${myvar}"
|
||||
OPTIND=0
|
||||
getopts "abc" myvar -abc
|
||||
echo "after reset: ${myvar}"
|
||||
|
||||
- name: "getopts: OPTERR non-numeric treated as enabled"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
OPTERR=abc
|
||||
getopts "a:" myvar -x
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: invalid variable name rejected"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
getopts "a" "123abc" -a
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "getopts: duplicate option char in optstring uses first definition"
|
||||
stdin: |
|
||||
getopts "aa:" myvar -a val
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: optstring is only a colon"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
getopts ":" myvar -a
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTARG: ${OPTARG-UNSET}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts: empty string argument is not an option"
|
||||
stdin: |
|
||||
getopts "a" myvar ""
|
||||
echo "Result: $?"
|
||||
echo "myvar: ${myvar}"
|
||||
echo "OPTIND: ${OPTIND}"
|
||||
|
||||
- name: "getopts writes through nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=result
|
||||
getopts "a:b" ref -a value
|
||||
echo "Result: $?"
|
||||
echo "result: ${result}"
|
||||
echo "ref: ${ref}"
|
||||
echo "OPTARG: ${OPTARG}"
|
||||
|
||||
- name: "getopts writes through subscripted nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
arr=(x x x)
|
||||
declare -n ref='arr[1]'
|
||||
getopts "a:b" ref -a value
|
||||
echo "Result: $?"
|
||||
echo "arr[1]: ${arr[1]}"
|
||||
echo "ref: ${ref}"
|
||||
@@ -0,0 +1,69 @@
|
||||
name: "Builtins: hash"
|
||||
cases:
|
||||
- name: "Non-existent program"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash non-existent-program
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "Re-hash non-existent program"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash -p /some/path non-existent-program
|
||||
hash -t non-existent-program && echo "1. Result: $?"
|
||||
hash non-existent-program && echo "2. Result: $?"
|
||||
hash -t non-existent-program && echo "3. Result: $?"
|
||||
|
||||
- name: "Existent program"
|
||||
stdin: |
|
||||
hash ls
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "Display not-yet-hashed program"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash -t ls
|
||||
echo "1. Result: $?"
|
||||
ls >/dev/null 2>&1
|
||||
hash -t ls
|
||||
echo "2. Result: $?"
|
||||
|
||||
- name: "Display hashed program"
|
||||
stdin: |
|
||||
hash -p /some/path somecmd && echo "1. Result: $?"
|
||||
hash -t somecmd && echo "2. Result: $?"
|
||||
|
||||
- name: "Display multiple hashed programs"
|
||||
stdin: |
|
||||
hash -p /some/path somecmd1 && echo "1. Result: $?"
|
||||
hash -p /some/path somecmd2 && echo "2. Result: $?"
|
||||
hash -t somecmd1 somecmd2 && echo "3. Result: $?"
|
||||
|
||||
- name: "Display -l path"
|
||||
stdin: |
|
||||
hash -p "/some/spaces path" somecmd && echo "1. Result: $?"
|
||||
hash -t somecmd && echo "2. Result: $?"
|
||||
hash -t -l somecmd && echo "3. Result: $?"
|
||||
|
||||
- name: "Remove"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash -p /some/path somecmd && echo "1. Result: $?"
|
||||
hash -t somecmd && echo "2. Result: $?"
|
||||
hash -d somecmd && echo "3. Result: $?"
|
||||
hash -t somecmd && echo "4. Result: $?"
|
||||
|
||||
- name: "Remove all"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash -p /some/path somecmd1 && echo "1. Result: $?"
|
||||
hash -p /some/path somecmd2 && echo "2. Result: $?"
|
||||
hash -r && echo "3. Result: $?"
|
||||
hash -t somecmd1 && echo "4. Result: $?"
|
||||
hash -t somecmd2 && echo "5. Result: $?"
|
||||
|
||||
- name: "Hash file path"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
hash /dev/null && echo "1. Result: $?"
|
||||
hash /dev/nulll && echo "2. Result: $?"
|
||||
@@ -0,0 +1,13 @@
|
||||
name: "Builtins: help"
|
||||
cases:
|
||||
- name: "Help"
|
||||
ignore_stdout: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
help
|
||||
|
||||
- name: "Topic-specific help"
|
||||
ignore_stdout: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
help echo
|
||||
@@ -0,0 +1,193 @@
|
||||
name: "Builtins: history"
|
||||
cases:
|
||||
- name: "basic history saving"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo something
|
||||
echo else
|
||||
|
||||
- name: "existing history file"
|
||||
pty: true
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
test_files:
|
||||
- path: "history-file.txt"
|
||||
contents: |
|
||||
a b c
|
||||
1 2 3
|
||||
ignore_stdout: true # Prompts and other don't quite line up
|
||||
stdin: |
|
||||
#expect-prompt
|
||||
history >history-output.txt
|
||||
#send:Enter
|
||||
#expect-prompt
|
||||
#send:Ctrl+D
|
||||
|
||||
- name: "existing history file with timestamps"
|
||||
pty: true
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
test_files:
|
||||
- path: "history-file.txt"
|
||||
contents: |
|
||||
#1750000000
|
||||
a b c
|
||||
#1750000001
|
||||
1 2 3
|
||||
ignore_stdout: true # Prompts and other don't quite line up
|
||||
stdin: |
|
||||
#expect-prompt
|
||||
history >history-output.txt
|
||||
#send:Enter
|
||||
#expect-prompt
|
||||
#send:Ctrl+D
|
||||
|
||||
- name: "existing history file with timestamps and HISTTIMEFORMAT"
|
||||
pty: true
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
HISTTIMEFORMAT: "%C "
|
||||
test_files:
|
||||
- path: "history-file.txt"
|
||||
contents: |
|
||||
#1750000000
|
||||
a b c
|
||||
#1750000001
|
||||
1 2 3
|
||||
ignore_stdout: true # Prompts and other don't quite line up
|
||||
stdin: |
|
||||
#expect-prompt
|
||||
#send:Ctrl+D
|
||||
|
||||
- name: "history N"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
history 2
|
||||
|
||||
- name: "history with duplicates"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd1
|
||||
: cmd1
|
||||
: cmd2
|
||||
: cmd1
|
||||
history
|
||||
|
||||
- name: "history -c"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
history -c
|
||||
history
|
||||
|
||||
- name: "history -d"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
history -d 1
|
||||
history
|
||||
|
||||
- name: "history -d: negative offset"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
history -d -2
|
||||
history
|
||||
|
||||
- name: "history -a"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo "[histfile]"
|
||||
cat $HISTFILE
|
||||
|
||||
history -a
|
||||
|
||||
echo "[histfile after -a]"
|
||||
cat $HISTFILE
|
||||
|
||||
history -a
|
||||
|
||||
echo "[histfile after second -a]"
|
||||
cat $HISTFILE
|
||||
|
||||
- name: "history -w"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
echo "[histfile]"
|
||||
cat $HISTFILE
|
||||
|
||||
history -w
|
||||
|
||||
echo "[histfile after -w]"
|
||||
cat $HISTFILE
|
||||
|
||||
history -w
|
||||
|
||||
echo "[histfile after second -w]"
|
||||
cat $HISTFILE
|
||||
|
||||
- name: "history -w with explicit file"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
history -w some-file.txt
|
||||
|
||||
- name: "history -s"
|
||||
known_failure: true # NOTE: bash excludes the history command itself from history
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
history -s a b c
|
||||
history
|
||||
|
||||
- name: "HISTCMD"
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
stdin: |
|
||||
: cmd1
|
||||
echo $HISTCMD
|
||||
: cmd2
|
||||
echo $HISTCMD
|
||||
: cmd3
|
||||
echo $HISTCMD
|
||||
|
||||
- name: "HISTTIMEFORMAT"
|
||||
known_failure: true # TODO(history): needs triage
|
||||
args: ["-o", "history"]
|
||||
env:
|
||||
HISTFILE: "history-file.txt"
|
||||
HISTTIMEFORMAT: "century=%C "
|
||||
stdin: |
|
||||
: cmd1
|
||||
: cmd2
|
||||
history
|
||||
|
||||
- name: "history file with unreadable lines"
|
||||
ignore_stderr: true # Ignore brush's warnings about unreadable lines
|
||||
stdin: |
|
||||
bash -c 'printf "\xff\xfe\xfd" >history-file.txt'
|
||||
HISTFILE=history-file.txt $0 -o history -c 'echo hi'
|
||||
@@ -0,0 +1,6 @@
|
||||
name: "Builtins: job-related builtins"
|
||||
cases:
|
||||
- name: "Basic async job"
|
||||
stdin: |
|
||||
echo hi &
|
||||
wait
|
||||
@@ -0,0 +1,27 @@
|
||||
name: "Builtins: kill"
|
||||
cases:
|
||||
- name: "kill -l"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
for i in $(seq 1 31); do kill -l $i; done
|
||||
# limit the number of signals to 31. Realtime signals are not implemented yet.
|
||||
for i in $(kill -l | sed -e "s/[[:digit:]]*)//g"); do echo $i; done | head -31
|
||||
# invalid option
|
||||
kill -l 9999
|
||||
kill -l HUP
|
||||
kill -l iNt
|
||||
kill -l int
|
||||
kill -l SIGHUP
|
||||
kill -l EXIT
|
||||
|
||||
- name: "kill -s"
|
||||
stdin: |
|
||||
kill -s USR1 $$
|
||||
|
||||
- name: "kill -n"
|
||||
stdin: |
|
||||
kill -n 9 $$
|
||||
|
||||
- name: "kill -sigspec"
|
||||
stdin: |
|
||||
kill -USR1 $$
|
||||
@@ -0,0 +1,28 @@
|
||||
name: "Builtins: let"
|
||||
cases:
|
||||
- name: "Basic let usage"
|
||||
stdin: |
|
||||
let 0; echo "0 => $?"
|
||||
let 1; echo "1 => $?"
|
||||
|
||||
let 0==0; echo "0==0 => $?"
|
||||
let 0!=0; echo "0!=0 => $?"
|
||||
|
||||
let 1 0; echo "1 0 => $?"
|
||||
let 0 1; echo "0 1 => $?"
|
||||
|
||||
- name: "let with assignment"
|
||||
stdin: |
|
||||
let x=10; echo "x=10 => $?; x==${x}"
|
||||
|
||||
- name: "let assignment through nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target=0
|
||||
declare -n ref=target
|
||||
let ref=42
|
||||
echo "target: $target"
|
||||
let ref+=8
|
||||
echo "target: $target"
|
||||
let ref++
|
||||
echo "target: $target"
|
||||
@@ -0,0 +1,21 @@
|
||||
name: "Builtins: local"
|
||||
cases:
|
||||
- name: "Basic local usage"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
local x=10
|
||||
echo "in myfunc: x==$x"
|
||||
}
|
||||
x=5
|
||||
echo "before call: x==$x"
|
||||
myfunc
|
||||
echo "after call: x==$x"
|
||||
|
||||
- name: "Local with empty array"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
local x=()
|
||||
declare -p x
|
||||
echo "x[0]: ${x[0]}"
|
||||
}
|
||||
myfunc
|
||||
@@ -0,0 +1,202 @@
|
||||
name: "Builtins: mapfile"
|
||||
cases:
|
||||
#
|
||||
# Basic functionality
|
||||
#
|
||||
- name: "mapfile basic"
|
||||
stdin: |
|
||||
mapfile arr < <(echo -e "a\nb\nc")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -t"
|
||||
stdin: |
|
||||
mapfile -t arr < <(echo -e "a\nb\nc")
|
||||
declare -p arr
|
||||
|
||||
- name: "readarray -t"
|
||||
stdin: |
|
||||
readarray -t arr < <(echo -e "a\nb\nc")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile default MAPFILE"
|
||||
stdin: |
|
||||
mapfile -t < <(echo -e "a\nb")
|
||||
declare -p MAPFILE
|
||||
|
||||
#
|
||||
# -O flag
|
||||
#
|
||||
- name: "mapfile -O starts at offset"
|
||||
stdin: |
|
||||
mapfile -t -O 3 arr < <(echo -e "a\nb\nc")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -O 0 preserves existing"
|
||||
stdin: |
|
||||
arr=(x y z w)
|
||||
mapfile -t -O 0 arr < <(echo -e "a\nb")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile without -O clears existing"
|
||||
stdin: |
|
||||
arr=(x y z w)
|
||||
mapfile -t arr < <(echo -e "a\nb")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -O preserves sparse array"
|
||||
stdin: |
|
||||
declare -a arr
|
||||
arr[0]=x
|
||||
arr[5]=y
|
||||
arr[10]=z
|
||||
mapfile -t -O 2 arr < <(echo -e "a\nb")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -O large offset"
|
||||
stdin: |
|
||||
mapfile -t -O 100 arr < <(echo -e "a\nb")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -O negative"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
mapfile -O -1 arr < /dev/null
|
||||
echo "exit: $?"
|
||||
|
||||
#
|
||||
# Variable state edge cases
|
||||
#
|
||||
- name: "mapfile into nonexistent variable"
|
||||
stdin: |
|
||||
mapfile -t newvar < <(echo -e "a\nb")
|
||||
declare -p newvar
|
||||
|
||||
- name: "mapfile into string variable"
|
||||
stdin: |
|
||||
myvar="hello"
|
||||
mapfile -t myvar < <(echo -e "a\nb")
|
||||
declare -p myvar
|
||||
|
||||
- name: "mapfile -O into string variable"
|
||||
stdin: |
|
||||
myvar="hello"
|
||||
mapfile -t -O 2 myvar < <(echo -e "a\nb")
|
||||
declare -p myvar
|
||||
|
||||
- name: "mapfile into associative array"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare -A myassoc
|
||||
mapfile -t myassoc < <(echo -e "a\nb")
|
||||
echo "exit: $?"
|
||||
|
||||
#
|
||||
# -d delimiter
|
||||
#
|
||||
- name: "mapfile -d custom delimiter"
|
||||
stdin: |
|
||||
mapfile -t -d 'Z' arr < <(echo -ne "helloZthereZgeneralZkenobi")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -d empty string (NUL)"
|
||||
stdin: |
|
||||
mapfile -t -d '' arr < <(printf 'a\0b\0c')
|
||||
declare -p arr
|
||||
|
||||
#
|
||||
# -n count
|
||||
#
|
||||
- name: "mapfile -n limits count"
|
||||
stdin: |
|
||||
mapfile -t -n 2 arr < <(echo -e "a\nb\nc\nd")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -n 0 no limit"
|
||||
stdin: |
|
||||
mapfile -t -n 0 arr < <(echo -e "a\nb\nc")
|
||||
declare -p arr
|
||||
|
||||
#
|
||||
# -s skip
|
||||
#
|
||||
- name: "mapfile -s"
|
||||
stdin: |
|
||||
mapfile -t -s 2 arr < <(echo -e "a\nb\nc\nd")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -s with -n"
|
||||
stdin: |
|
||||
mapfile -t -s 1 -n 2 arr < <(echo -e "a\nb\nc\nd\ne")
|
||||
declare -p arr
|
||||
|
||||
#
|
||||
# -u file descriptor
|
||||
#
|
||||
- name: "mapfile -u reads from fd"
|
||||
stdin: |
|
||||
exec 3< <(echo -e "a\nb\nc")
|
||||
mapfile -t -u 3 arr
|
||||
exec 3<&-
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile -u invalid fd"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
mapfile -u 99 arr
|
||||
echo "exit: $?"
|
||||
|
||||
#
|
||||
# Combinations
|
||||
#
|
||||
- name: "mapfile -O with -s and -n"
|
||||
stdin: |
|
||||
mapfile -t -O 10 -s 1 -n 2 arr < <(echo -e "a\nb\nc\nd\ne")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile multiple invocations without -O"
|
||||
stdin: |
|
||||
mapfile -t arr < <(echo -e "a\nb")
|
||||
mapfile -t arr < <(echo -e "x\ny\nz")
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile multiple invocations with -O"
|
||||
stdin: |
|
||||
mapfile -t arr < <(echo -e "a\nb")
|
||||
mapfile -t -O 2 arr < <(echo -e "x\ny\nz")
|
||||
declare -p arr
|
||||
|
||||
#
|
||||
# Empty input
|
||||
#
|
||||
- name: "mapfile empty input"
|
||||
stdin: |
|
||||
mapfile -t arr < /dev/null
|
||||
declare -p arr 2>/dev/null || echo "arr not set"
|
||||
|
||||
- name: "mapfile empty input clears existing"
|
||||
stdin: |
|
||||
arr=(x y z)
|
||||
mapfile -t arr < /dev/null
|
||||
declare -p arr 2>/dev/null || echo "arr not set"
|
||||
|
||||
- name: "mapfile -O empty input preserves"
|
||||
stdin: |
|
||||
arr=(x y z)
|
||||
mapfile -t -O 0 arr < /dev/null
|
||||
declare -p arr
|
||||
|
||||
- name: "mapfile into nameref variable"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -a target
|
||||
declare -n ref=target
|
||||
mapfile -t ref < <(echo -e "a\nb\nc")
|
||||
declare -p target
|
||||
|
||||
- name: "mapfile -O into nameref variable"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target=(x y)
|
||||
declare -n ref=target
|
||||
mapfile -t -O 2 ref < <(echo -e "a\nb")
|
||||
declare -p target
|
||||
@@ -0,0 +1,347 @@
|
||||
name: "Builtins: printf"
|
||||
|
||||
cases:
|
||||
# Basic functionality tests
|
||||
- name: "One-variable printf"
|
||||
stdin: |
|
||||
printf "%s" "0"
|
||||
|
||||
- name: "Basic printf"
|
||||
stdin: |
|
||||
printf "%s, %s" "Hello" "world"
|
||||
|
||||
- name: "printf -v"
|
||||
stdin: |
|
||||
printf -v myvar "%s, %s" "Hello" "world"
|
||||
echo "myvar: '${myvar}'"
|
||||
|
||||
- name: "printf -v with array index"
|
||||
stdin: |
|
||||
declare -a myarray=()
|
||||
printf -v 'myarray[5]' "%s, %s" "Hello" "world"
|
||||
declare | grep myarray
|
||||
|
||||
- name: "printf with -v as a format arg"
|
||||
stdin: |
|
||||
printf "%s\n" "-v"
|
||||
|
||||
# Bash-specific %q format (shell-safe quoting)
|
||||
- name: "printf %q"
|
||||
stdin: |
|
||||
echo "[1]"
|
||||
printf "%q" 'TEXT'; echo
|
||||
|
||||
echo "[2]"
|
||||
printf "%q" '$VAR'; echo
|
||||
|
||||
echo "[3]"
|
||||
printf "%q" '"'; echo
|
||||
|
||||
- name: "printf ~%q"
|
||||
stdin: |
|
||||
echo "[1]"
|
||||
printf "~%q" 'TEXT'; echo
|
||||
|
||||
echo "[2]"
|
||||
printf "~%q" '$VAR'; echo
|
||||
|
||||
echo "[3]"
|
||||
printf "~%q" '"'; echo
|
||||
|
||||
# =================================================================
|
||||
# STANDARD C PRINTF FORMAT SPECIFIERS
|
||||
# These should work with external printf and bash builtin printf
|
||||
# =================================================================
|
||||
|
||||
# Integer formatting tests
|
||||
- name: "printf %d with integers"
|
||||
stdin: |
|
||||
printf "%d\n" 42
|
||||
printf "%d\n" -17
|
||||
printf "%d\n" 0
|
||||
|
||||
- name: "printf %o (octal)"
|
||||
stdin: |
|
||||
printf "%o\n" 42
|
||||
printf "%o\n" 8
|
||||
printf "%o\n" 255
|
||||
|
||||
- name: "printf %x and %X (hex)"
|
||||
stdin: |
|
||||
printf "%x\n" 255
|
||||
printf "%X\n" 255
|
||||
printf "%x\n" 42
|
||||
|
||||
# Floating point tests
|
||||
- name: "printf %f (float)"
|
||||
stdin: |
|
||||
printf "%f\n" 3.14159
|
||||
printf "%.2f\n" 3.14159
|
||||
printf "%6.2f\n" 3.14159
|
||||
|
||||
- name: "printf %e and %E (scientific)"
|
||||
stdin: |
|
||||
printf "%e\n" 1234.5
|
||||
printf "%E\n" 1234.5
|
||||
|
||||
- name: "printf %g and %G (general)"
|
||||
stdin: |
|
||||
printf "%g\n" 1234.5
|
||||
printf "%G\n" 0.00012345
|
||||
|
||||
# Character formatting
|
||||
- name: "printf %c (character)"
|
||||
stdin: |
|
||||
printf "%c\n" 65
|
||||
printf "%c\n" 97
|
||||
printf "%c%c%c\n" 72 101 121
|
||||
|
||||
# Width and precision tests
|
||||
- name: "printf width formatting"
|
||||
stdin: |
|
||||
printf "%5s|\n" "hi"
|
||||
printf "%-5s|\n" "hi"
|
||||
printf "%05d|\n" 42
|
||||
|
||||
- name: "printf precision with strings"
|
||||
stdin: |
|
||||
printf "%.3s\n" "hello"
|
||||
printf "%10.3s\n" "hello"
|
||||
|
||||
# Multiple arguments and reuse
|
||||
- name: "printf argument reuse"
|
||||
stdin: |
|
||||
printf "%s %s %s\n" "one" "two"
|
||||
printf "%d %d %d\n" 1 2
|
||||
|
||||
# Special characters and escapes
|
||||
- name: "printf with escape sequences"
|
||||
stdin: |
|
||||
printf "hello\nworld\n"
|
||||
printf "tab\there\n"
|
||||
printf "quote: \"\n"
|
||||
|
||||
- name: "printf with backslash escapes"
|
||||
stdin: |
|
||||
printf "\\n\\t\\r\\\\\n"
|
||||
|
||||
# Missing arguments (bash handles gracefully)
|
||||
- name: "printf missing arguments"
|
||||
stdin: |
|
||||
printf "%s %s\n" "only_one"
|
||||
|
||||
# Empty and edge cases
|
||||
- name: "printf with empty format"
|
||||
stdin: |
|
||||
printf ""
|
||||
|
||||
- name: "printf with just format string"
|
||||
stdin: |
|
||||
printf "just text\n"
|
||||
|
||||
# Type conversion tests (safe cases only)
|
||||
- name: "printf valid string to number conversion"
|
||||
stdin: |
|
||||
printf "%d\n" "42"
|
||||
printf "%d\n" "0"
|
||||
|
||||
- name: "printf with percent literal"
|
||||
stdin: |
|
||||
printf "100%% complete\n"
|
||||
|
||||
- name: "printf %b (backslash escape expansion)"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%b\n" "hello\\nworld"
|
||||
printf "%b\n" "tab\\there"
|
||||
printf "%b\n" "quote\\\"here"
|
||||
|
||||
- name: "printf %b with field width"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%10b|\n" "hello\\nworld"
|
||||
printf "%-10b|\n" "tab\\there"
|
||||
|
||||
- name: "printf %T (date/time formatting)"
|
||||
known_failure: true # Not supported by uucore
|
||||
stdin: |
|
||||
printf "%(%Y-%m-%d)T\n" 1234567890
|
||||
|
||||
# Test argument reuse with bash-specific formats
|
||||
- name: "printf %q argument reuse"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%q %q %q\n" "one with spaces" "two"
|
||||
|
||||
- name: "printf %b argument reuse"
|
||||
stdin: |
|
||||
printf "%b %b %b\n" "one\\ntwo" "three"
|
||||
|
||||
# Test C-style constant interpretation
|
||||
- name: "printf with quoted character arguments"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%d\n" "'A"
|
||||
printf "%d\n" '"'
|
||||
|
||||
- name: "printf with plus/minus signs in numeric arguments"
|
||||
stdin: |
|
||||
printf "%d\n" "+42"
|
||||
printf "%d\n" "-17"
|
||||
|
||||
# =================================================================
|
||||
# ADDITIONAL STANDARD FORMAT SPECIFIERS
|
||||
# More comprehensive coverage of printf(3) man page features
|
||||
# =================================================================
|
||||
|
||||
# Additional format specifiers from man page
|
||||
- name: "printf %i (signed integer)"
|
||||
stdin: |
|
||||
printf "%i\n" 42
|
||||
printf "%i\n" -17
|
||||
|
||||
- name: "printf %u (unsigned integer)"
|
||||
stdin: |
|
||||
printf "%u\n" 42
|
||||
printf "%u\n" 4294967295
|
||||
|
||||
# Advanced width and precision
|
||||
- name: "printf with asterisk width and precision"
|
||||
stdin: |
|
||||
printf "%*s|\n" 10 "hello"
|
||||
printf "%.*s|\n" 3 "hello world"
|
||||
printf "%*.*s|\n" 10 3 "hello world"
|
||||
|
||||
- name: "printf with zero precision and zero arg"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%.0d\n" 0
|
||||
|
||||
- name: "printf with zero precision"
|
||||
stdin: |
|
||||
printf "%.0d\n" 42
|
||||
|
||||
# Flag characters
|
||||
- name: "printf with flag characters"
|
||||
stdin: |
|
||||
printf "%+d\n" 42
|
||||
printf "% d\n" 42
|
||||
printf "%#x\n" 255
|
||||
printf "%#o\n" 64
|
||||
|
||||
# Left justification
|
||||
- name: "printf left justification"
|
||||
stdin: |
|
||||
printf "%-10s|\n" "left"
|
||||
printf "%-10d|\n" 42
|
||||
|
||||
# Zero padding
|
||||
- name: "printf zero padding"
|
||||
stdin: |
|
||||
printf "%010d\n" 42
|
||||
printf "%010.2f\n" 3.14
|
||||
|
||||
# Combining flags
|
||||
- name: "printf combining flags"
|
||||
stdin: |
|
||||
printf "%+010d\n" 42
|
||||
printf "%-+10d|\n" 42
|
||||
|
||||
# More precision tests
|
||||
- name: "printf precision with integers"
|
||||
stdin: |
|
||||
printf "%.5d\n" 42
|
||||
printf "%.5o\n" 42
|
||||
printf "%.5x\n" 42
|
||||
|
||||
# Large numbers and edge cases
|
||||
- name: "printf with large numbers"
|
||||
stdin: |
|
||||
printf "%d\n" 2147483647
|
||||
printf "%u\n" 4294967295
|
||||
printf "%x\n" 4294967295
|
||||
|
||||
# More character and escape testing
|
||||
- name: "printf with special characters"
|
||||
stdin: |
|
||||
printf "%c\n" 0
|
||||
printf "%c\n" 32
|
||||
printf "%c\n" 127
|
||||
|
||||
# Negative numbers with different formats
|
||||
- name: "printf negative numbers"
|
||||
stdin: |
|
||||
printf "%d\n" -1
|
||||
printf "%o\n" -1
|
||||
printf "%x\n" -1
|
||||
printf "%u\n" -1
|
||||
|
||||
# Empty string handling
|
||||
- name: "printf with empty strings"
|
||||
stdin: |
|
||||
printf "%s|\n" ""
|
||||
printf "%5s|\n" ""
|
||||
printf "%.5s|\n" ""
|
||||
|
||||
# More empty string handling
|
||||
- name: "printf with empty strings for numbers"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
printf "%d" ""
|
||||
printf "%x" ""
|
||||
|
||||
# Scientific notation edge cases
|
||||
- name: "printf scientific notation edge cases"
|
||||
stdin: |
|
||||
printf "%e\n" 0.0
|
||||
printf "%E\n" 0.0
|
||||
printf "%g\n" 0.0000001
|
||||
printf "%G\n" 1000000.0
|
||||
|
||||
# Mixed format strings
|
||||
- name: "printf mixed formats in one string"
|
||||
stdin: |
|
||||
printf "Dec: %d, Hex: %x, Oct: %o, Char: %c\n" 65 65 65 65
|
||||
|
||||
# Field width larger than content
|
||||
- name: "printf wide fields"
|
||||
stdin: |
|
||||
printf "%20s|\n" "short"
|
||||
printf "%20d|\n" 42
|
||||
|
||||
# Return value testing (should always be 0 for successful printf)
|
||||
- name: "printf return value"
|
||||
stdin: |
|
||||
printf "test"
|
||||
echo " exit: $?"
|
||||
|
||||
# Error handling tests - marked as known failures due to external printf differences
|
||||
- name: "printf with invalid format"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
printf "%z\n" "test"
|
||||
|
||||
- name: "printf with no arguments"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
printf
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "printf string to number conversion with invalid input"
|
||||
known_failure: true # Need to investigate
|
||||
stdin: |
|
||||
printf "%d\n" "abc"
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "printf %q with empty string"
|
||||
stdin: |
|
||||
printf '%q\n' ''
|
||||
printf '~%q\n' ''
|
||||
|
||||
- name: "printf -v through nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=target
|
||||
printf -v ref "formatted %d" 42
|
||||
echo "target: $target"
|
||||
echo "ref: $ref"
|
||||
@@ -0,0 +1,8 @@
|
||||
name: "Builtins: PS4"
|
||||
cases:
|
||||
- name: "PS4 expansion"
|
||||
stdin: |
|
||||
PS4='+$FUNCNAME '
|
||||
bar() { true; }
|
||||
foo() { set -x; bar; set +x; }
|
||||
foo
|
||||
@@ -0,0 +1,65 @@
|
||||
name: "Builtins: pushd/popd/dirs"
|
||||
incompatible_configs: ["sh"]
|
||||
cases:
|
||||
- name: "Basic pushd usage"
|
||||
stdin: |
|
||||
cd /
|
||||
pushd /
|
||||
echo $?
|
||||
echo $PWD
|
||||
pushd /usr
|
||||
dirs
|
||||
echo $?
|
||||
echo $PWD
|
||||
popd
|
||||
echo $?
|
||||
echo $PWD
|
||||
popd
|
||||
echo $?
|
||||
|
||||
- name: "pushd without dir change"
|
||||
stdin: |
|
||||
cd /
|
||||
pushd -n /usr
|
||||
dirs
|
||||
|
||||
- name: "popd without dir change"
|
||||
stdin: |
|
||||
cd /
|
||||
pushd /
|
||||
pushd /usr
|
||||
popd -n
|
||||
dirs
|
||||
|
||||
- name: "popd with empty stack"
|
||||
ignore_stderr: true
|
||||
stdin: popd
|
||||
|
||||
- name: "pushd to non-existent dir"
|
||||
ignore_stderr: true
|
||||
stdin: pushd /non-existent-dir
|
||||
|
||||
- name: "basic dirs usage"
|
||||
stdin: |
|
||||
cd /
|
||||
dirs
|
||||
|
||||
- name: "dirs with tilde replacement"
|
||||
env:
|
||||
HOME: /usr
|
||||
stdin: |
|
||||
echo "HOME: $HOME"
|
||||
|
||||
cd ~
|
||||
echo "New PWD: $PWD"
|
||||
|
||||
dirs
|
||||
dirs -l
|
||||
|
||||
- name: "dirs to clear"
|
||||
stdin: |
|
||||
cd /usr
|
||||
pushd /usr
|
||||
pushd /
|
||||
dirs -c
|
||||
dirs
|
||||
@@ -0,0 +1,70 @@
|
||||
name: "Builtins: pwd"
|
||||
cases:
|
||||
- name: "Basic pwd usage"
|
||||
stdin: |
|
||||
cd /
|
||||
pwd
|
||||
echo "Result: $?"
|
||||
#
|
||||
cd usr
|
||||
pwd
|
||||
echo "Result: $?"
|
||||
|
||||
- name: "pwd -LP"
|
||||
stdin: |
|
||||
mkdir -p ./level1/level2/level3
|
||||
cd level1
|
||||
ln -s ./level2/level3 ./symlink
|
||||
|
||||
(
|
||||
cd ./symlink
|
||||
basename $(pwd)
|
||||
basename $(pwd -L)
|
||||
basename $(pwd -P)
|
||||
)
|
||||
(
|
||||
cd ./symlink
|
||||
export PWD=
|
||||
basename $(pwd)
|
||||
basename $(pwd -L)
|
||||
basename $(pwd -P)
|
||||
)
|
||||
(
|
||||
cd ./symlink
|
||||
export PWD=
|
||||
# start a shell without $PWD
|
||||
(
|
||||
basename $(pwd)
|
||||
basename $(pwd -L)
|
||||
basename $(pwd -P)
|
||||
)
|
||||
)
|
||||
|
||||
cd ~
|
||||
pwd
|
||||
pwd -L
|
||||
pwd -P
|
||||
|
||||
- name: "pwd with moved dir"
|
||||
known_failure: true # Needs investigation
|
||||
stdin: |
|
||||
root=$(pwd)
|
||||
|
||||
mkdir -p ${root}/subdir
|
||||
cd ${root}/subdir
|
||||
mv ${root}/subdir ${root}/renamed
|
||||
|
||||
echo "pwd -L: $(basename $(pwd -L))"
|
||||
echo "pwd -P: $(basename $(pwd -P))"
|
||||
|
||||
- name: "pwd with removed dir"
|
||||
known_failure: true # Needs investigation
|
||||
stdin: |
|
||||
root=$(pwd)
|
||||
|
||||
mkdir -p ${root}/subdir
|
||||
cd ${root}/subdir
|
||||
rmdir ${root}/subdir
|
||||
|
||||
echo "pwd -L: $(basename $(pwd -L))"
|
||||
echo "pwd -P: $(basename $(pwd -P))"
|
||||
@@ -0,0 +1,392 @@
|
||||
name: "Builtins: read"
|
||||
cases:
|
||||
- name: "Basic read usage from file"
|
||||
test_files:
|
||||
- path: "data.txt"
|
||||
contents: |
|
||||
a 1
|
||||
b 2
|
||||
stdin: |
|
||||
while read name num; do echo "Hello, $name => $num"; done < data.txt
|
||||
echo "Exited loop; name='$name', num='$num'"
|
||||
|
||||
- name: "Basic read usage from file and no var name"
|
||||
test_files:
|
||||
- path: "data.txt"
|
||||
contents: |
|
||||
a 1
|
||||
b 2
|
||||
stdin: |
|
||||
while read; do echo "reply: '$REPLY'"; done < data.txt
|
||||
echo "Exited loop; reply='$REPLY'"
|
||||
|
||||
- name: "Basic read usage with empty file"
|
||||
stdin: |
|
||||
while read name num; do echo "Hello, $name => $num"; done < /dev/null
|
||||
declare -p name num
|
||||
|
||||
while read; do echo "reply: '$REPLY'"; done < /dev/null
|
||||
declare -p REPLY
|
||||
|
||||
while read -a arr; do declare -p arr; done < /dev/null
|
||||
declare -p arr
|
||||
|
||||
- name: "Basic read usage from pipe"
|
||||
stdin: |
|
||||
echo "1."
|
||||
(echo a; echo b) | while read name; do echo "Hello, $name"; done
|
||||
|
||||
echo "2."
|
||||
(echo "a b") | while read name; do echo "Hello, 1:$name REPLY:$REPLY"; done
|
||||
|
||||
echo "3."
|
||||
(echo "a b") | while read -a arr; do declare -p arr; done
|
||||
|
||||
- name: "read with closed stdin"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
echo 1 | read <&-
|
||||
echo "result: $?"
|
||||
|
||||
- name: "read from here string"
|
||||
stdin: |
|
||||
read myvar <<< "hello"
|
||||
echo "myvar: ${myvar}"
|
||||
|
||||
- name: "read from process substitution"
|
||||
stdin: |
|
||||
read myvar < <(echo hello)
|
||||
echo "myvar: ${myvar}"
|
||||
|
||||
- name: "read with custom IFS"
|
||||
stdin: |
|
||||
content=" a b c "
|
||||
while IFS= read line; do
|
||||
echo "LINE: '$line'"
|
||||
done <<<"${content}"
|
||||
|
||||
- name: "read text with tabs and custom IFS"
|
||||
stdin: |
|
||||
while IFS="" read myvar; do
|
||||
echo "myvar1: |${myvar}|"
|
||||
done < <(printf "a\tb\nc d\te\n")
|
||||
|
||||
- name: "read with empty entries"
|
||||
stdin: |
|
||||
(echo "x"; echo ""; echo "y"; echo ""; echo "") | while read line; do echo "LINE: '$line'"; done
|
||||
echo 'x,,y,z' | (IFS=',' read READ1 READ2 READ3 READ4; declare -p READ1; declare -p READ2; declare -p READ3; declare -p READ4)
|
||||
|
||||
- name: "read -a with empty lines"
|
||||
stdin: |
|
||||
(echo "hi"; echo ""; echo "there"; echo ""; echo "you") | (read -a READ; declare -p READ)
|
||||
(echo -e "hi\t\tthere\t\tyou") | (read -a READ -d $'\t'; declare -p READ)
|
||||
|
||||
- name: "read -a with empty interior field"
|
||||
stdin: |
|
||||
echo -e -n "hi||there||you" | (IFS='|' read -a READ; declare -p READ)
|
||||
echo -e -n "hi there you " | (IFS=' ' read -a READ; declare -p READ)
|
||||
echo -e -n "hi there you " | (read -a READ; declare -p READ)
|
||||
|
||||
- name: "read -a with empty leading field"
|
||||
stdin: |
|
||||
echo -e -n "|hi|there|you" | (IFS='|' read -a READ; declare -p READ)
|
||||
|
||||
- name: "read -a with empty trailing field"
|
||||
stdin: |
|
||||
echo -e -n "hi|there|you|" | (IFS='|' read -a READ; declare -p READ)
|
||||
|
||||
- name: "read -a with empty entries + empty delimiter"
|
||||
stdin: |
|
||||
(echo "hi"; echo ""; echo "there"; echo ""; echo "you") | (read -a READ -d ''; declare -p READ)
|
||||
echo -e -n "hi\n\nthere\n\nyou\n" | (read -a READ -d ''; declare -p READ)
|
||||
|
||||
- name: "read -a with empty entries + empty delimiter + custom IFS"
|
||||
stdin: |
|
||||
(echo 'x,,y,z'; echo 'w,v'; echo ''; echo ''; echo 'm') | (IFS=',' read -d "" -a READ; declare -p READ)
|
||||
|
||||
- name: "read with empty delimiter"
|
||||
min_oracle_version: 5.2 # \n renders differently in older shell versions
|
||||
stdin: |
|
||||
echo x | (read -d ""; declare -p REPLY)
|
||||
|
||||
# Timeout tests (read -t)
|
||||
- name: "read -t 0 with data available in pipe"
|
||||
# TODO(read):
|
||||
# This test is racy - poll(0) may check the pipe before echo's data arrives.
|
||||
# Bash consistently wins this race; our implementation sometimes doesn't.
|
||||
# Worth investigating.
|
||||
skip: true
|
||||
stdin: |
|
||||
echo "test" | { read -t 0; echo "exit: $?"; }
|
||||
|
||||
- name: "read -t 0 with no data (empty pipe)"
|
||||
skip: true # TODO(read): see above
|
||||
stdin: |
|
||||
# Closed pipe with no data
|
||||
: | { read -t 0; echo "exit: $?"; }
|
||||
|
||||
- name: "read -t with data immediately available"
|
||||
stdin: |
|
||||
echo "hello world" | { read -t 1 line; echo "exit: $?, line: '$line'"; }
|
||||
|
||||
- name: "read -t with fractional timeout and data available"
|
||||
stdin: |
|
||||
echo "test data" | { read -t 0.5 var; echo "exit: $?, var: '$var'"; }
|
||||
|
||||
- name: "read -t with regular file (always ready)"
|
||||
test_files:
|
||||
- path: "data.txt"
|
||||
contents: |
|
||||
line1
|
||||
line2
|
||||
stdin: |
|
||||
read -t 0 < data.txt; echo "exit: $?"
|
||||
read -t 1 line < data.txt; echo "exit: $?, line: '$line'"
|
||||
|
||||
- name: "read -t negative timeout is invalid"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
read -t -1
|
||||
echo "exit: $?"
|
||||
|
||||
- name: "read -t with -n option"
|
||||
stdin: |
|
||||
echo "abcdef" | { read -t 1 -n 3 var; echo "exit: $?, var: '$var'"; }
|
||||
|
||||
- name: "read -t 0 with here string"
|
||||
stdin: |
|
||||
read -t 0 <<< "hello"; echo "exit: $?"
|
||||
read -t 1 var <<< "hello"; echo "exit: $?, var: '$var'"
|
||||
|
||||
# Raw mode tests (read -r)
|
||||
- name: "read -r preserves backslashes"
|
||||
stdin: |
|
||||
echo 'hello\nworld' | { read -r line; echo "line: '$line'"; }
|
||||
echo 'path\\to\\file' | { read -r line; echo "line: '$line'"; }
|
||||
|
||||
- name: "read without -r processes escape sequences"
|
||||
stdin: |
|
||||
echo 'hello\nworld' | { read line; echo "line: '$line'"; }
|
||||
echo 'a\\b' | { read line; echo "line: '$line'"; }
|
||||
|
||||
- name: "read -r with trailing backslash"
|
||||
stdin: |
|
||||
printf 'line with trailing\\' | { read -r line; echo "line: '$line'"; }
|
||||
|
||||
- name: "read without -r line continuation"
|
||||
stdin: |
|
||||
# Backslash-newline continues to next line
|
||||
printf 'first\\\nsecond' | { read line; echo "line: '$line'"; }
|
||||
|
||||
- name: "read -r does not do line continuation"
|
||||
stdin: |
|
||||
printf 'first\\\nsecond' | { read -r line; echo "line: '$line'"; }
|
||||
|
||||
# -N vs -n option tests
|
||||
- name: "read -n respects delimiter"
|
||||
stdin: |
|
||||
# -n stops at delimiter even before char count
|
||||
echo "ab:cd" | { read -n 5 var; echo "var: '$var'"; }
|
||||
|
||||
- name: "read -N ignores delimiter"
|
||||
skip: true # TODO(read): test sometimes results in printf error
|
||||
stdin: |
|
||||
# -N reads exactly N chars, delimiter treated as regular char
|
||||
printf "ab:cd" | { read -N 4 var; echo "var: '$var'"; }
|
||||
|
||||
- name: "read -N does not split by IFS"
|
||||
stdin: |
|
||||
# -N should not split result by IFS
|
||||
printf "a b c" | { read -N 5 var; echo "var: '$var'"; }
|
||||
|
||||
- name: "read -N with array"
|
||||
stdin: |
|
||||
# With -a and -N, entire input goes to single array element
|
||||
printf "a b c" | { read -N 5 -a arr; declare -p arr; }
|
||||
|
||||
# Exit code tests
|
||||
- name: "read exit code on success"
|
||||
stdin: |
|
||||
echo "data" | { read var; echo "exit: $?"; }
|
||||
|
||||
- name: "read exit code on EOF"
|
||||
stdin: |
|
||||
# EOF returns non-zero
|
||||
read var < /dev/null; echo "exit: $?"
|
||||
|
||||
- name: "read exit code on EOF with partial data"
|
||||
stdin: |
|
||||
# EOF with partial data still returns non-zero but assigns
|
||||
printf "partial" | { read var; echo "exit: $?, var: '$var'"; }
|
||||
|
||||
# IFS edge cases
|
||||
- name: "read with IFS containing both whitespace and non-whitespace"
|
||||
stdin: |
|
||||
echo "a:b c:d" | (IFS=': ' read v1 v2 v3 v4; echo "v1='$v1' v2='$v2' v3='$v3' v4='$v4'")
|
||||
|
||||
- name: "read with consecutive non-whitespace delimiters"
|
||||
stdin: |
|
||||
echo "a::b" | (IFS=':' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
||||
|
||||
- name: "read assignment to fewer variables than fields"
|
||||
stdin: |
|
||||
echo "a b c d e" | { read v1 v2; echo "v1='$v1' v2='$v2'"; }
|
||||
|
||||
- name: "read assignment to more variables than fields"
|
||||
stdin: |
|
||||
echo "a b" | { read v1 v2 v3 v4; echo "v1='$v1' v2='$v2' v3='$v3' v4='$v4'"; }
|
||||
|
||||
- name: "read last variable preserves original delimiters"
|
||||
stdin: |
|
||||
# Last variable should get remainder with original delimiters, not re-joined with space
|
||||
echo "x:y:z:w" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
||||
echo "x:y::z:w" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
||||
echo "a:b c:d e" | (IFS=': ' read v1 v2; echo "v1='$v1' v2='$v2'")
|
||||
|
||||
- name: "read with consecutive delimiters at field boundary"
|
||||
stdin: |
|
||||
# When field limit causes boundary at consecutive delimiters,
|
||||
# extra delimiters should be preserved in remainder.
|
||||
echo "x::y" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
||||
echo "x:::y" | (IFS=':' read a b; echo "a='$a' b='$b'")
|
||||
# Also test with more than 2 variables
|
||||
echo "a::b:c" | (IFS=':' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
||||
|
||||
- name: "read with empty IFS preserves entire line"
|
||||
stdin: |
|
||||
# Empty IFS means no splitting - entire line goes to first variable
|
||||
echo "a b c" | (IFS='' read v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'")
|
||||
echo "a:b:c" | (IFS='' read v1 v2; echo "v1='$v1' v2='$v2'")
|
||||
# With array
|
||||
echo "a b c" | (IFS='' read -a arr; declare -p arr)
|
||||
|
||||
- name: "read REPLY preserves leading and trailing whitespace"
|
||||
stdin: |
|
||||
# When no variable names given, REPLY should preserve whitespace
|
||||
read <<< ' hello world '
|
||||
echo "REPLY='$REPLY'"
|
||||
read <<< ' tab separated '
|
||||
echo "REPLY='$REPLY'"
|
||||
|
||||
- name: "read -a clears pre-existing array elements"
|
||||
stdin: |
|
||||
# Array should be cleared before assignment, not merged
|
||||
arr=(old1 old2 old3 old4 old5)
|
||||
read -a arr <<< 'new1 new2'
|
||||
declare -p arr
|
||||
|
||||
- name: "read -N still processes backslashes without -r"
|
||||
stdin: |
|
||||
# -N ignores delimiter but still does backslash processing
|
||||
printf 'a\\nb' | { read -N 4 var; echo "var='$var'"; }
|
||||
printf 'a\\\\b' | { read -N 4 var; echo "var='$var'"; }
|
||||
|
||||
- name: "read -r -N for truly raw fixed-length reads"
|
||||
stdin: |
|
||||
# -r -N should preserve backslashes exactly
|
||||
printf 'a\\nb' | { read -r -N 4 var; echo "var='$var'"; }
|
||||
printf 'a\\\\b' | { read -r -N 4 var; echo "var='$var'"; }
|
||||
|
||||
- name: "read -u with invalid file descriptor"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
read -u 99 var
|
||||
echo "exit: $?"
|
||||
|
||||
- name: "read into readonly variable"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
readonly myvar="original"
|
||||
echo "test" | read myvar
|
||||
echo "exit: $?, myvar='$myvar'"
|
||||
|
||||
- name: "read with mixed IFS into fewer variables"
|
||||
stdin: |
|
||||
# Complex IFS with fewer variables - verify remainder handling
|
||||
echo 'x: y : z : w' | (IFS=': ' read a b; echo "a='$a' b='$b'")
|
||||
|
||||
# Backslash escape counting tests (bash counts OUTPUT chars, not INPUT)
|
||||
- name: "read -n counts output chars after escape processing"
|
||||
stdin: |
|
||||
# Bash counts OUTPUT characters toward -n limit (after escape processing).
|
||||
# Input: a, \, b, c = 4 input bytes. With -n 3:
|
||||
# 'a' (output 1), '\b' → 'b' (output 2), 'c' (output 3) = "abc"
|
||||
printf 'a\\bc' | { read -n 3 var; echo "var='$var'"; }
|
||||
# Input: \, \, a, b = 4 input bytes. With -n 3:
|
||||
# '\\' → '\' (output 1), 'a' (output 2), 'b' (output 3) = "\ab"
|
||||
printf '\\\\ab' | { read -n 3 var; echo "var='$var'"; }
|
||||
|
||||
- name: "read -n with backslash-newline continuation"
|
||||
stdin: |
|
||||
# Backslash-newline is line continuation (consumes both, outputs nothing).
|
||||
# Input: a, b, \, \n, c = 5 input bytes. With -n 3:
|
||||
# 'a' (output 1), 'b' (output 2), '\n' → continuation, 'c' (output 3) = "abc"
|
||||
printf 'ab\\\nc' | { read -n 3 var; echo "var='$var'"; }
|
||||
|
||||
- name: "read trailing backslash discarded on EOF"
|
||||
stdin: |
|
||||
# Bash discards a pending backslash when EOF is reached
|
||||
printf 'ab\\' | { read var; echo "var='$var'"; }
|
||||
printf 'ab\\' | { read -n 5 var; echo "var='$var'"; }
|
||||
|
||||
# Prompt tests (read -p)
|
||||
- name: "read -p reads input correctly"
|
||||
stdin: |
|
||||
# Test that -p option reads input correctly (prompt display behavior
|
||||
# differs between bash and brush when stdin is not a terminal)
|
||||
read -p "Enter: " var <<< "hello"
|
||||
echo "var='$var'"
|
||||
|
||||
# File descriptor tests (read -u)
|
||||
- name: "read -u with valid file descriptor"
|
||||
stdin: |
|
||||
# Read from a specific file descriptor
|
||||
exec 3<<< "from fd 3"
|
||||
read -u 3 var
|
||||
echo "var='$var'"
|
||||
exec 3<&-
|
||||
|
||||
- name: "read -u combined with other options"
|
||||
stdin: |
|
||||
exec 4<<< "hello world test"
|
||||
read -u 4 -a arr
|
||||
declare -p arr
|
||||
exec 4<&-
|
||||
|
||||
# Silent mode tests (read -s)
|
||||
- name: "read -s with pipe input"
|
||||
stdin: |
|
||||
# Silent mode should still read input correctly (just not echo it)
|
||||
echo "secret" | { read -s var; echo "var='$var'"; }
|
||||
|
||||
- name: "read -s with multiple variables"
|
||||
stdin: |
|
||||
echo "a b c" | { read -s v1 v2 v3; echo "v1='$v1' v2='$v2' v3='$v3'"; }
|
||||
|
||||
- name: "read into nameref variable"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=result
|
||||
read ref <<< "input_value"
|
||||
echo "result: $result"
|
||||
echo "ref: $ref"
|
||||
|
||||
- name: "read -a into nameref array"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=myarr
|
||||
read -a ref <<< "one two three"
|
||||
echo "myarr[0]: ${myarr[0]}"
|
||||
echo "myarr[1]: ${myarr[1]}"
|
||||
echo "myarr[2]: ${myarr[2]}"
|
||||
echo "ref[1]: ${ref[1]}"
|
||||
|
||||
- name: "read into subscripted nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
arr=(a b c)
|
||||
declare -n ref='arr[1]'
|
||||
read ref <<< "replaced"
|
||||
echo "arr[0]: ${arr[0]}"
|
||||
echo "arr[1]: ${arr[1]}"
|
||||
echo "arr[2]: ${arr[2]}"
|
||||
@@ -0,0 +1,53 @@
|
||||
name: "Builtins: readonly"
|
||||
cases:
|
||||
- name: "making var readonly"
|
||||
stdin: |
|
||||
my_var="value"
|
||||
readonly my_var
|
||||
|
||||
echo "Invoking declare -p..."
|
||||
declare -p my_var
|
||||
|
||||
- name: "using readonly with value"
|
||||
stdin: |
|
||||
readonly my_var="my_value"
|
||||
|
||||
echo "Invoking declare -p..."
|
||||
declare -p my_var
|
||||
|
||||
- name: "readonly array after append"
|
||||
stdin: |
|
||||
ARR=()
|
||||
f() {
|
||||
ARR+=("a" "b")
|
||||
readonly ARR
|
||||
echo "${#ARR[@]}"
|
||||
}
|
||||
f
|
||||
|
||||
- name: "readonly on nameref makes nameref readonly"
|
||||
known_failure: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
target="value"
|
||||
declare -n ref=target
|
||||
readonly ref
|
||||
declare -p ref
|
||||
declare -p target
|
||||
# Changing what ref points to should fail
|
||||
declare -n ref=other 2>/dev/null
|
||||
echo "exit: $?"
|
||||
# But target should still be writable
|
||||
target="new_value"
|
||||
echo "target: $target"
|
||||
|
||||
- name: "readonly target through nameref assignment"
|
||||
known_failure: true
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
target="value"
|
||||
readonly target
|
||||
declare -n ref=target
|
||||
ref="new_value" 2>/dev/null
|
||||
echo "exit: $?"
|
||||
echo "target: $target"
|
||||
@@ -0,0 +1,158 @@
|
||||
name: "Builtins: return"
|
||||
cases:
|
||||
- name: "Return outside function or script"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
return 42
|
||||
|
||||
- name: "Return outside function with stderr to /dev/full"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
# /dev/full may not be available on all platforms.
|
||||
[[ -e /dev/full ]] || exit 0
|
||||
return 2>/dev/full
|
||||
echo "exit: $?"
|
||||
|
||||
- name: "Return in directly invoked script"
|
||||
ignore_stderr: true
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
return 42
|
||||
echo "Got past return"
|
||||
args: ["./script.sh"]
|
||||
|
||||
- name: "Return from sourced script"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
return 42
|
||||
echo "Got past return"
|
||||
stdin: |
|
||||
source script.sh
|
||||
|
||||
- name: "Return from subshell"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
(return)
|
||||
echo "Got past subshell"
|
||||
exit 42
|
||||
|
||||
- name: "Return from nested sourced script"
|
||||
test_files:
|
||||
- path: "inner.sh"
|
||||
contents: |
|
||||
return 42
|
||||
echo "Got past inner return"
|
||||
- path: "outer.sh"
|
||||
contents: |
|
||||
source inner.sh
|
||||
echo "Got to end of outer script"
|
||||
stdin: |
|
||||
source outer.sh
|
||||
|
||||
- name: "Return in function"
|
||||
test_files:
|
||||
- path: "script.sh"
|
||||
contents: |
|
||||
myfunc() {
|
||||
echo "In myfunc()"
|
||||
return 5
|
||||
echo "Should not get here"
|
||||
}
|
||||
echo "Calling myfunc()..."
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
args: ["./script.sh"]
|
||||
|
||||
- name: "Return in for loop in function"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
for i in 1 2 3; do
|
||||
echo "In myfunc: $i"
|
||||
return 5
|
||||
done
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return in arithmetic for loop in function"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
for ((i=0; i < 5; i++)); do
|
||||
echo "In myfunc: $i"
|
||||
return 5
|
||||
done
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return in while loop in function"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
i=0
|
||||
while [[ $i -lt 5 ]]; do
|
||||
echo "In myfunc: $i"
|
||||
return 33
|
||||
i=$((i+1))
|
||||
done
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return in case"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
case 1 in
|
||||
1)
|
||||
echo "In case"
|
||||
return 5
|
||||
echo "Should not get here"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return in brace group"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
{
|
||||
echo "In brace group"
|
||||
return 5
|
||||
echo "Should not get here"
|
||||
}
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return in and/or"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
echo "In and/or" && return 5 && echo "Should not get here"
|
||||
}
|
||||
|
||||
myfunc
|
||||
echo "Returned: $?"
|
||||
|
||||
- name: "Return from nested clauses"
|
||||
stdin: |
|
||||
myfunc() {
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
*)
|
||||
shift 2 || {
|
||||
echo "Returning"
|
||||
return 5
|
||||
}
|
||||
echo "Shifted and fell through"
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
myfunc a b c
|
||||
@@ -0,0 +1,103 @@
|
||||
name: "Builtins: set"
|
||||
cases:
|
||||
- name: "set with no args"
|
||||
stdin: |
|
||||
MYVARIABLE=VALUE
|
||||
set > set-output.txt
|
||||
grep MYVARIABLE set-output.txt
|
||||
|
||||
# Remove set-output.txt to avoid it being byte-for-byte compared.
|
||||
rm set-output.txt
|
||||
|
||||
- name: "Basic set usage"
|
||||
stdin: |
|
||||
set a b c d
|
||||
echo ${*}
|
||||
|
||||
- name: "set with options"
|
||||
stdin: |
|
||||
function dumpopts {
|
||||
# Dump the options
|
||||
echo "[Options: $1]"
|
||||
echo "set options: " $-
|
||||
shopt -p -o pipefail
|
||||
}
|
||||
|
||||
set -e -u -o pipefail
|
||||
dumpopts enabled
|
||||
echo '*: ' $*
|
||||
|
||||
set +e +u +o pipefail
|
||||
dumpopts disabled
|
||||
echo '*: ' $*
|
||||
|
||||
- name: "set with multiple combined options"
|
||||
stdin: |
|
||||
function dumpopts {
|
||||
# Dump the options
|
||||
echo "[Options: $1]"
|
||||
echo "set options: " $-
|
||||
shopt -p -o pipefail
|
||||
}
|
||||
|
||||
set -euo pipefail
|
||||
dumpopts enabled
|
||||
echo '$*: ' $*
|
||||
|
||||
set +euo pipefail
|
||||
dumpopts disabled
|
||||
echo '$*: ' $*
|
||||
|
||||
- name: "set clearing args"
|
||||
stdin: |
|
||||
set a b c
|
||||
echo ${*}
|
||||
set a
|
||||
echo ${*}
|
||||
|
||||
- name: "set with -"
|
||||
stdin: |
|
||||
set - a b c
|
||||
echo "args: " ${*}
|
||||
set -
|
||||
echo "args: " ${*}
|
||||
|
||||
- name: "set with --"
|
||||
stdin: |
|
||||
set -- a b c
|
||||
echo "args: " ${*}
|
||||
set --
|
||||
echo "args: " ${*}
|
||||
|
||||
- name: "set with option-looking args"
|
||||
stdin: |
|
||||
set -- a -v
|
||||
echo ${*}
|
||||
|
||||
set - a -v
|
||||
echo ${*}
|
||||
|
||||
set a -v
|
||||
echo ${*}
|
||||
|
||||
set -- a +x
|
||||
echo ${*}
|
||||
|
||||
set - a +x
|
||||
echo ${*}
|
||||
|
||||
set a +x
|
||||
echo ${*}
|
||||
|
||||
- name: "function-local set"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
function f() {
|
||||
local -
|
||||
set -x
|
||||
echo "In func"
|
||||
}
|
||||
|
||||
echo "Before func"
|
||||
f
|
||||
echo "After func"
|
||||
@@ -0,0 +1,77 @@
|
||||
name: "Builtins: shopt"
|
||||
cases:
|
||||
- name: "shopt defaults"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
min_oracle_version: 5.3 # Options changed in bash 5.3
|
||||
incompatible_os: ["fedora"] # Fedora enables "syslog-history" option
|
||||
stdin: |
|
||||
shopt | sort | grep -v extglob
|
||||
|
||||
- name: "shopt interactive defaults"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
min_oracle_version: 5.3 # Options changed in bash 5.3
|
||||
incompatible_os: ["fedora"] # Fedora enables "syslog-history" option
|
||||
pty: true
|
||||
args: ["-i", "-c", "shopt | sort | grep -v extglob"]
|
||||
|
||||
- name: "shopt -o defaults"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
min_oracle_version: 5.3 # Options changed in bash 5.3
|
||||
stdin: |
|
||||
shopt -o | sort
|
||||
|
||||
- name: "shopt -o interactive defaults"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
min_oracle_version: 5.3 # Options changed in bash 5.3
|
||||
pty: true
|
||||
args: ["-i", "-c", "shopt -o | sort | grep -v monitor"]
|
||||
|
||||
- name: "extglob defaults"
|
||||
known_failure: true # TODO(patterns): we force this setting on in our shell
|
||||
stdin: |
|
||||
shopt extglob
|
||||
|
||||
- name: "extglob interactive defaults"
|
||||
pty: true
|
||||
args: ["-i", "-c", "shopt extglob"]
|
||||
known_failure: true
|
||||
|
||||
- name: "shopt -o interactive monitor default"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
min_oracle_version: 5.3 # Options changed in bash 5.3
|
||||
pty: true
|
||||
args: ["-i", "-c", "shopt -o monitor"]
|
||||
|
||||
- name: "shopt toggle"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
stdin: |
|
||||
echo "Setting checkwinsize"
|
||||
shopt -s checkwinsize
|
||||
|
||||
echo "Displaying checkwinsize"
|
||||
shopt checkwinsize
|
||||
shopt -p checkwinsize
|
||||
|
||||
echo "Unsetting checkwinsize"
|
||||
shopt -u checkwinsize
|
||||
|
||||
echo "Displaying checkwinsize"
|
||||
shopt checkwinsize
|
||||
shopt -p checkwinsize
|
||||
|
||||
- name: "shopt -o usage"
|
||||
ignore_whitespace: true # Spacing changes across versions
|
||||
stdin: |
|
||||
echo "Setting emacs"
|
||||
shopt -o -s emacs
|
||||
|
||||
echo "Displaying emacs"
|
||||
shopt -o emacs
|
||||
shopt -o -p emacs
|
||||
|
||||
echo "Unsetting emacs"
|
||||
shopt -o -u emacs
|
||||
|
||||
echo "Displaying emacs"
|
||||
shopt -o emacs
|
||||
shopt -o -p emacs
|
||||
@@ -0,0 +1,357 @@
|
||||
name: "Builtins: test"
|
||||
cases:
|
||||
- name: "test: = operator"
|
||||
stdin: |
|
||||
shopt -u nocasematch
|
||||
test "ab" = "ab" && echo "ab = ab"
|
||||
test "ab" = "AB" && echo "ab = AB"
|
||||
test "ab" = "cd" && echo "ab = cd"
|
||||
test "ab" = "a?" && echo "ab = a?"
|
||||
|
||||
shopt -s nocasematch
|
||||
test "ab" = "ab" && echo "ab = ab"
|
||||
test "ab" = "AB" && echo "ab = AB"
|
||||
test "ab" = "cd" && echo "ab = cd"
|
||||
test "ab" = "a?" && echo "ab = a?"
|
||||
|
||||
- name: "test: == operator"
|
||||
stdin: |
|
||||
shopt -u nocasematch
|
||||
test "ab" == "ab" && echo "ab == ab"
|
||||
test "ab" == "AB" && echo "ab == AB"
|
||||
test "ab" == "cd" && echo "ab == cd"
|
||||
test "ab" == "a?" && echo "ab == a?"
|
||||
|
||||
shopt -s nocasematch
|
||||
test "ab" == "ab" && echo "ab == ab"
|
||||
test "ab" == "AB" && echo "ab == AB"
|
||||
test "ab" == "cd" && echo "ab == cd"
|
||||
test "ab" == "a?" && echo "ab == a?"
|
||||
|
||||
- name: "test: files refer to same device and inode"
|
||||
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: "test: 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: "test: 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: "test: parens"
|
||||
stdin: |
|
||||
test \( "a" = "a" \) && echo "Parens work"
|
||||
|
||||
- name: "test: -- string"
|
||||
stdin: |
|
||||
test --
|
||||
echo "Result(test): $?"
|
||||
|
||||
[ -- ]
|
||||
echo "Result([): $?"
|
||||
|
||||
- name: "test: invalid operators"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
test - -
|
||||
echo "Result: $?"
|
||||
|
||||
# String operators
|
||||
- name: "test: != operator"
|
||||
stdin: |
|
||||
test "ab" != "cd" && echo "ab != cd"
|
||||
test "ab" != "ab" && echo "ab != ab"
|
||||
test "" != "x" && echo "empty != x"
|
||||
|
||||
- name: "test: -z operator (zero length)"
|
||||
stdin: |
|
||||
test -z "" && echo "-z empty"
|
||||
test -z "x" && echo "-z x"
|
||||
unset VAR
|
||||
test -z "$VAR" && echo "-z unset"
|
||||
|
||||
- name: "test: -n operator (non-zero length)"
|
||||
stdin: |
|
||||
test -n "x" && echo "-n x"
|
||||
test -n "" && echo "-n empty"
|
||||
VAR="hello"
|
||||
test -n "$VAR" && echo "-n set"
|
||||
|
||||
- name: "test: < operator (string sort before)"
|
||||
stdin: |
|
||||
test "a" \< "b" && echo "a < b"
|
||||
test "b" \< "a" && echo "b < a"
|
||||
test "abc" \< "abd" && echo "abc < abd"
|
||||
test "a" \< "a" && echo "a < a"
|
||||
|
||||
- name: "test: > operator (string sort after)"
|
||||
stdin: |
|
||||
test "b" \> "a" && echo "b > a"
|
||||
test "a" \> "b" && echo "a > b"
|
||||
test "abd" \> "abc" && echo "abd > abc"
|
||||
test "a" \> "a" && echo "a > a"
|
||||
|
||||
# Arithmetic comparison operators
|
||||
- name: "test: -eq operator"
|
||||
stdin: |
|
||||
test 5 -eq 5 && echo "5 -eq 5"
|
||||
test 5 -eq 6 && echo "5 -eq 6"
|
||||
test -1 -eq -1 && echo "-1 -eq -1"
|
||||
test 0 -eq 0 && echo "0 -eq 0"
|
||||
|
||||
- name: "test: -ne operator"
|
||||
stdin: |
|
||||
test 5 -ne 6 && echo "5 -ne 6"
|
||||
test 5 -ne 5 && echo "5 -ne 5"
|
||||
test -1 -ne 1 && echo "-1 -ne 1"
|
||||
|
||||
- name: "test: -lt operator"
|
||||
stdin: |
|
||||
test 3 -lt 5 && echo "3 -lt 5"
|
||||
test 5 -lt 3 && echo "5 -lt 3"
|
||||
test 5 -lt 5 && echo "5 -lt 5"
|
||||
test -2 -lt -1 && echo "-2 -lt -1"
|
||||
|
||||
- name: "test: -le operator"
|
||||
stdin: |
|
||||
test 3 -le 5 && echo "3 -le 5"
|
||||
test 5 -le 5 && echo "5 -le 5"
|
||||
test 6 -le 5 && echo "6 -le 5"
|
||||
test -2 -le -2 && echo "-2 -le -2"
|
||||
|
||||
- name: "test: -gt operator"
|
||||
stdin: |
|
||||
test 5 -gt 3 && echo "5 -gt 3"
|
||||
test 3 -gt 5 && echo "3 -gt 5"
|
||||
test 5 -gt 5 && echo "5 -gt 5"
|
||||
test -1 -gt -2 && echo "-1 -gt -2"
|
||||
|
||||
- name: "test: -ge operator"
|
||||
stdin: |
|
||||
test 5 -ge 3 && echo "5 -ge 3"
|
||||
test 5 -ge 5 && echo "5 -ge 5"
|
||||
test 3 -ge 5 && echo "3 -ge 5"
|
||||
test -1 -ge -1 && echo "-1 -ge -1"
|
||||
|
||||
# File existence and type operators
|
||||
- name: "test: -e operator (file exists)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -e testfile && echo "-e existing file"
|
||||
test -e nonexistent && echo "-e nonexistent"
|
||||
mkdir testdir
|
||||
test -e testdir && echo "-e existing dir"
|
||||
|
||||
- name: "test: -a operator (file exists, deprecated)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -a testfile && echo "-a existing file"
|
||||
test -a nonexistent && echo "-a nonexistent"
|
||||
|
||||
- name: "test: -f operator (regular file)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -f testfile && echo "-f regular file"
|
||||
mkdir testdir
|
||||
test -f testdir && echo "-f directory"
|
||||
test -f nonexistent && echo "-f nonexistent"
|
||||
|
||||
- name: "test: -d operator (directory)"
|
||||
stdin: |
|
||||
mkdir testdir
|
||||
test -d testdir && echo "-d directory"
|
||||
touch testfile
|
||||
test -d testfile && echo "-d regular file"
|
||||
test -d nonexistent && echo "-d nonexistent"
|
||||
|
||||
- name: "test: -h and -L operators (symbolic link)"
|
||||
stdin: |
|
||||
touch target
|
||||
ln -s target symlink
|
||||
test -h symlink && echo "-h symlink"
|
||||
test -L symlink && echo "-L symlink"
|
||||
test -h target && echo "-h regular file"
|
||||
test -L target && echo "-L regular file"
|
||||
test -h nonexistent && echo "-h nonexistent"
|
||||
|
||||
- name: "test: -p operator (named pipe/FIFO)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -p testfile && echo "-p regular file"
|
||||
test -p nonexistent && echo "-p nonexistent"
|
||||
|
||||
- name: "test: -S operator (socket)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -S testfile && echo "-S regular file"
|
||||
test -S nonexistent && echo "-S nonexistent"
|
||||
|
||||
- name: "test: -b operator (block device)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -b testfile && echo "-b regular file"
|
||||
test -b nonexistent && echo "-b nonexistent"
|
||||
# Block devices exist in /dev but may vary by system
|
||||
|
||||
- name: "test: -c operator (character device)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -c testfile && echo "-c regular file"
|
||||
test -c nonexistent && echo "-c nonexistent"
|
||||
|
||||
# File permission operators
|
||||
- name: "test: -r operator (readable)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -r testfile && echo "-r readable"
|
||||
test -r nonexistent && echo "-r nonexistent"
|
||||
|
||||
- name: "test: -w operator (writable)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -w testfile && echo "-w writable"
|
||||
test -w nonexistent && echo "-w nonexistent"
|
||||
|
||||
- name: "test: -x operator (executable)"
|
||||
stdin: |
|
||||
touch testfile
|
||||
test -x testfile && echo "-x executable"
|
||||
|
||||
- name: "test: -s operator (size greater than zero)"
|
||||
stdin: |
|
||||
echo "content" > nonempty
|
||||
test -s nonempty && echo "-s nonempty"
|
||||
touch empty
|
||||
test -s empty && echo "-s empty"
|
||||
test -s nonexistent && echo "-s nonexistent"
|
||||
|
||||
# Variable and option operators
|
||||
- name: "test: -v operator (variable is set)"
|
||||
stdin: |
|
||||
VAR="hello"
|
||||
test -v VAR && echo "-v set variable"
|
||||
unset VAR
|
||||
test -v VAR && echo "-v unset variable"
|
||||
VAR=""
|
||||
test -v VAR && echo "-v empty variable"
|
||||
|
||||
- name: "test: -R operator (nameref variable)"
|
||||
stdin: |
|
||||
VAR="value"
|
||||
declare -n REF=VAR
|
||||
test -R REF && echo "-R nameref"
|
||||
test -R VAR && echo "-R regular var"
|
||||
unset REF VAR
|
||||
|
||||
- name: "test: -o operator (shell option)"
|
||||
stdin: |
|
||||
set -e
|
||||
test -o errexit && echo "-o errexit enabled"
|
||||
set +e
|
||||
test -o errexit && echo "-o errexit disabled"
|
||||
test -o nonexistentoption && echo "-o nonexistent"
|
||||
|
||||
# Logical operators
|
||||
- name: "test: ! operator (negation)"
|
||||
stdin: |
|
||||
test ! "a" = "b" && echo "! a=b"
|
||||
test ! "a" = "a" && echo "! a=a"
|
||||
test ! -e nonexistent && echo "! -e nonexistent"
|
||||
test ! -e / && echo "! -e /"
|
||||
|
||||
- name: "test: -a operator (logical AND)"
|
||||
stdin: |
|
||||
test "a" = "a" -a "b" = "b" && echo "true -a true"
|
||||
test "a" = "a" -a "b" = "c" && echo "true -a false"
|
||||
test "a" = "b" -a "c" = "c" && echo "false -a true"
|
||||
test "a" = "b" -a "c" = "d" && echo "false -a false"
|
||||
|
||||
- name: "test: -o operator (logical OR)"
|
||||
stdin: |
|
||||
test "a" = "a" -o "b" = "b" && echo "true -o true"
|
||||
test "a" = "a" -o "b" = "c" && echo "true -o false"
|
||||
test "a" = "b" -o "c" = "c" && echo "false -o true"
|
||||
test "a" = "b" -o "c" = "d" && echo "false -o false"
|
||||
|
||||
- name: "test: complex logical expressions"
|
||||
stdin: |
|
||||
test \( "a" = "a" -o "b" = "c" \) -a "d" = "d" && echo "complex 1"
|
||||
test ! \( "a" = "b" \) && echo "complex 2"
|
||||
test "a" = "a" -a \( "b" = "b" -o "c" = "d" \) && echo "complex 3"
|
||||
|
||||
# Edge cases
|
||||
- name: "test: single argument (non-empty string is true)"
|
||||
stdin: |
|
||||
test "hello" && echo "non-empty is true"
|
||||
test "" && echo "empty is true"
|
||||
test "0" && echo "zero string is true"
|
||||
|
||||
- name: "test: no arguments"
|
||||
stdin: |
|
||||
test
|
||||
echo "no args: $?"
|
||||
|
||||
- name: "test: special characters in strings"
|
||||
stdin: |
|
||||
test "hello world" = "hello world" && echo "spaces match"
|
||||
test "a b" = "a b" && echo "tabs match"
|
||||
test 'a"b' = 'a"b' && echo "quotes match"
|
||||
test "a'b" = "a'b" && echo "single quotes match"
|
||||
|
||||
- name: "test: arithmetic comparison with spaces/tabs in operand"
|
||||
stdin: |
|
||||
# Leading/trailing spaces
|
||||
test 0 -eq ' 0'
|
||||
echo "leading space: $?"
|
||||
|
||||
test 0 -eq '0 '
|
||||
echo "trailing space: $?"
|
||||
|
||||
test 0 -eq ' 0 '
|
||||
echo "both spaces: $?"
|
||||
|
||||
# Tabs
|
||||
test 0 -eq ' 0'
|
||||
echo "leading tab: $?"
|
||||
|
||||
test 0 -eq '0 '
|
||||
echo "trailing tab: $?"
|
||||
|
||||
- name: "test: arithmetic comparison with newline in operand"
|
||||
min_oracle_version: "5.3"
|
||||
stdin: |
|
||||
# Newline trimming seems to have changed in or around bash 5.3
|
||||
test 0 -eq ' 0
|
||||
'
|
||||
echo "newline trailing: $?"
|
||||
|
||||
- name: "test: -t operator with whitespace in fd"
|
||||
stdin: |
|
||||
# -t operator should trim whitespace before parsing fd number
|
||||
# These should all behave identically (false since not a terminal)
|
||||
test -t 0
|
||||
r1=$?
|
||||
test -t ' 0'
|
||||
r2=$?
|
||||
test -t '0 '
|
||||
r3=$?
|
||||
test -t ' 0 '
|
||||
r4=$?
|
||||
|
||||
# All should have the same result
|
||||
[ $r1 -eq $r2 ] && [ $r2 -eq $r3 ] && [ $r3 -eq $r4 ] && echo "All -t results match"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
name: "Builtins: times"
|
||||
cases:
|
||||
- name: "Basic usage"
|
||||
ignore_stdout: true
|
||||
stdin: |
|
||||
times
|
||||
@@ -0,0 +1,724 @@
|
||||
name: "Builtins: trap"
|
||||
cases:
|
||||
#
|
||||
# trap -l signal listing
|
||||
#
|
||||
- name: "trap -l - lists all signal names"
|
||||
stdin: |
|
||||
# Just verify it outputs something reasonable - check common signals exist
|
||||
trap -l | grep -qE 'HUP|SIGHUP' && echo "has HUP"
|
||||
trap -l | grep -qE 'INT|SIGINT' && echo "has INT"
|
||||
trap -l | grep -qE 'TERM|SIGTERM' && echo "has TERM"
|
||||
|
||||
#
|
||||
# Basic trap registration
|
||||
#
|
||||
- name: "trap registration"
|
||||
stdin: |
|
||||
trap "echo 1" SIGINT
|
||||
trap "echo 2" SIGINT
|
||||
trap -p INT
|
||||
|
||||
trap "echo 3" int
|
||||
trap -p INT
|
||||
|
||||
trap "echo 4" 2
|
||||
trap -p INT
|
||||
|
||||
- name: "trap unregistering"
|
||||
stdin: |
|
||||
echo "[Case 1]"
|
||||
trap "echo 1" SIGINT
|
||||
trap SIGINT
|
||||
trap -p INT
|
||||
|
||||
echo "[Case 2]"
|
||||
trap "echo 2" SIGINT
|
||||
trap - SIGINT
|
||||
trap -p INT
|
||||
|
||||
- name: "trap EXIT"
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
trap -p EXIT
|
||||
|
||||
- name: "trap EXIT: status code handling"
|
||||
stdin: |
|
||||
trap 'echo "[exit]: \$?: $?"' EXIT
|
||||
trap -p EXIT
|
||||
false
|
||||
|
||||
- name: "trap DEBUG"
|
||||
stdin: |
|
||||
trap 'echo [command: ${BASH_COMMAND}]' DEBUG
|
||||
trap -p DEBUG
|
||||
|
||||
echo First
|
||||
echo Second
|
||||
false
|
||||
|
||||
- name: "trap ERR - registration only"
|
||||
stdin: |
|
||||
trap "echo [err]" ERR
|
||||
trap -p ERR
|
||||
|
||||
- name: "trap ERR - basic invocation"
|
||||
stdin: |
|
||||
trap 'echo "ERR: $?"' ERR
|
||||
false
|
||||
echo "continues"
|
||||
false
|
||||
echo "continues again"
|
||||
|
||||
- name: "trap ERR - not triggered in conditional"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
if false; then
|
||||
echo "then"
|
||||
else
|
||||
echo "else"
|
||||
fi
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - not triggered with && short-circuit"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
false && echo "not printed"
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - not triggered with ||"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
false || echo "fallback"
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - not triggered with negation"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
! false
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - not triggered in while condition"
|
||||
stdin: |
|
||||
trap 'echo "ERR"' ERR
|
||||
while false; do
|
||||
echo "loop body"
|
||||
done
|
||||
echo "after while"
|
||||
|
||||
- name: "trap ERR - not triggered in until condition"
|
||||
stdin: |
|
||||
trap 'echo "ERR"' ERR
|
||||
count=0
|
||||
until false; do
|
||||
((count++))
|
||||
if ((count > 2)); then break; fi
|
||||
done
|
||||
echo "after until"
|
||||
|
||||
- name: "trap ERR - not triggered as part of || list"
|
||||
stdin: |
|
||||
trap 'echo "ERR"' ERR
|
||||
false || true || false
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - not triggered for negated command"
|
||||
stdin: |
|
||||
trap 'echo "ERR"' ERR
|
||||
if ! true; then
|
||||
echo "then"
|
||||
fi
|
||||
! false
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - triggered after && chain completes then fails"
|
||||
stdin: |
|
||||
trap 'echo "ERR trapped"' ERR
|
||||
true && true
|
||||
false
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - execution doesn't affect control flow"
|
||||
stdin: |
|
||||
trap 'echo "ERR"' ERR
|
||||
result=0
|
||||
false || result=$?
|
||||
echo "result: $result"
|
||||
|
||||
- name: "trap ERR - multiple failures trigger multiple traps"
|
||||
stdin: |
|
||||
count=0
|
||||
trap '((count++)); echo "ERR $count"' ERR
|
||||
false
|
||||
false
|
||||
false
|
||||
echo "total: $count"
|
||||
|
||||
- name: "trap ERR - has access to exit status"
|
||||
stdin: |
|
||||
trap 'echo "ERR with status: $?"' ERR
|
||||
(exit 42)
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - has access to BASH_COMMAND"
|
||||
known_failure: true # TODO(traps): ERR trap basic execution not implemented
|
||||
stdin: |
|
||||
trap 'echo "ERR for: $BASH_COMMAND"' ERR
|
||||
nonexistent_command_xyz 2>/dev/null
|
||||
echo "after"
|
||||
|
||||
- name: "trap ERR - has access to LINENO"
|
||||
known_failure: true # TODO(traps): ERR trap basic execution not implemented
|
||||
stdin: |
|
||||
trap 'echo "ERR at line: $LINENO"' ERR
|
||||
false
|
||||
echo "after"
|
||||
|
||||
- name: "return in EXIT trap within function"
|
||||
known_failure: true # TODO(traps): return in trap handler behavior differs
|
||||
stdin: |
|
||||
myfunc() {
|
||||
trap 'return 0' EXIT
|
||||
false
|
||||
echo "should not print in func"
|
||||
}
|
||||
myfunc
|
||||
echo "after func"
|
||||
|
||||
- name: "subshell inherits signal traps (for trap -p display)"
|
||||
stdin: |
|
||||
trap 'echo "[parent int]"' INT
|
||||
trap 'echo "[parent term]"' TERM
|
||||
trap -p INT
|
||||
echo "---"
|
||||
(trap -p INT; trap -p TERM)
|
||||
|
||||
- name: "subshell can override inherited trap"
|
||||
stdin: |
|
||||
trap 'echo "[parent int]"' INT
|
||||
trap -p INT
|
||||
echo "---"
|
||||
(trap 'echo "[subshell int]"' INT; trap -p INT)
|
||||
|
||||
- name: "subshell inherits EXIT trap"
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
trap -p EXIT
|
||||
echo "---"
|
||||
(trap -p EXIT)
|
||||
|
||||
- name: "subshell inherits DEBUG trap"
|
||||
stdin: |
|
||||
trap 'echo "[debug]"' DEBUG
|
||||
trap -p DEBUG
|
||||
echo "---"
|
||||
(trap -p DEBUG)
|
||||
|
||||
- name: "subshell does not execute inherited DEBUG trap"
|
||||
stdin: |
|
||||
trap 'echo "[debug]"' DEBUG
|
||||
echo "parent"
|
||||
echo "---"
|
||||
(echo "subshell")
|
||||
echo "back in parent"
|
||||
|
||||
- name: "subshell can set its own EXIT trap"
|
||||
known_failure: true # TODO(traps): EXIT trap in subshell
|
||||
stdin: |
|
||||
trap 'echo "[parent exit]"' EXIT
|
||||
echo "parent"
|
||||
(trap 'echo "[subshell exit]"' EXIT; echo "subshell")
|
||||
echo "back in parent"
|
||||
|
||||
- name: "subshell inherited EXIT trap does not fire"
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
echo "parent"
|
||||
(echo "subshell")
|
||||
echo "back in parent"
|
||||
|
||||
#
|
||||
# Subshell trap inheritance in command substitution
|
||||
#
|
||||
- name: "command substitution inherits signal trap (trap -p display)"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
result=$(trap -p INT)
|
||||
echo "$result"
|
||||
|
||||
- name: "command substitution does not fire inherited signal trap"
|
||||
known_failure: true # TODO(signals): signal handling in command substitution
|
||||
stdin: |
|
||||
trap 'echo "[int]" >&2' INT
|
||||
result=$(kill -INT $$; echo "after kill")
|
||||
echo "result: $result"
|
||||
|
||||
- name: "command substitution can set its own trap"
|
||||
stdin: |
|
||||
trap 'echo "[parent int]"' INT
|
||||
result=$(trap 'echo "[subshell int]"' INT; trap -p INT)
|
||||
echo "$result"
|
||||
|
||||
- name: "command substitution EXIT trap not inherited from parent"
|
||||
stdin: |
|
||||
trap 'echo "[parent exit]"' EXIT
|
||||
result=$(trap -p EXIT; echo "inside")
|
||||
echo "result: $result"
|
||||
|
||||
- name: "command substitution can have its own EXIT trap"
|
||||
known_failure: true # TODO: EXIT trap in command substitution subshell
|
||||
stdin: |
|
||||
trap 'echo "[parent exit]"' EXIT
|
||||
result=$(trap 'echo "[sub exit]"' EXIT; echo "inside")
|
||||
echo "result: $result"
|
||||
|
||||
- name: "nested command substitution trap inheritance"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
outer=$(inner=$(trap -p INT); echo "inner: $inner")
|
||||
echo "outer: $outer"
|
||||
|
||||
#
|
||||
# Subshell trap inheritance in pipelines
|
||||
#
|
||||
- name: "pipeline component inherits signal trap (trap -p display)"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap -p INT | cat
|
||||
|
||||
- name: "pipeline component can override trap"
|
||||
stdin: |
|
||||
trap 'echo "[parent int]"' INT
|
||||
{ trap 'echo "[pipe int]"' INT; trap -p INT; } | cat
|
||||
|
||||
- name: "pipeline EXIT trap behavior"
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
{ trap -p EXIT; echo "in pipe"; } | cat
|
||||
echo "after pipe"
|
||||
|
||||
- name: "multiple pipeline stages trap inheritance"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap -p INT | cat | cat
|
||||
|
||||
#
|
||||
# Subshell trap inheritance in process substitution
|
||||
#
|
||||
- name: "process substitution inherits signal trap"
|
||||
known_failure: true # TODO: process substitution trap inheritance
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
cat <(trap -p INT)
|
||||
|
||||
- name: "process substitution output trap inheritance"
|
||||
skip: true # TODO: process substitution trap inheritance / inconsistent across distros
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
echo "data" > >(trap -p INT; cat > /dev/null)
|
||||
# Give time for the process substitution to complete
|
||||
wait
|
||||
|
||||
- name: "process substitution can set own EXIT trap"
|
||||
known_failure: true # TODO: EXIT trap in process substitution
|
||||
stdin: |
|
||||
trap 'echo "[parent exit]"' EXIT
|
||||
cat <(trap 'echo "[sub exit]"' EXIT; echo "inside")
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# Subshell trap inheritance in background jobs
|
||||
#
|
||||
- name: "background job inherits signal trap (trap -p display)"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap -p INT &
|
||||
wait
|
||||
|
||||
- name: "background job does not fire inherited signal trap"
|
||||
known_failure: true # TODO(signals): background job signal handling
|
||||
stdin: |
|
||||
trap 'echo "[int]" >&2' INT
|
||||
{ kill -INT $$; echo "after kill"; } &
|
||||
wait
|
||||
echo "main done"
|
||||
|
||||
- name: "background job EXIT trap behavior"
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
{ trap -p EXIT; echo "bg job"; } &
|
||||
wait
|
||||
echo "main continues"
|
||||
|
||||
- name: "background job can set own traps"
|
||||
stdin: |
|
||||
trap 'echo "[parent int]"' INT
|
||||
{ trap 'echo "[bg int]"' INT; trap -p INT; } &
|
||||
wait
|
||||
trap -p INT
|
||||
|
||||
#
|
||||
# Coproc trap inheritance (all coproc tests consolidated here)
|
||||
#
|
||||
# Note: "coproc - inherits signal trap" test is skipped due to bash version
|
||||
# differences. Older bash resets traps in coproc, newer bash (5.3.9) inherits them.
|
||||
# This causes inconsistent test results across CI runners.
|
||||
- name: "coproc - EXIT trap behavior"
|
||||
skip: true # TODO: this test is too inconsistent
|
||||
min_oracle_version: "5.3.0"
|
||||
incompatible_os:
|
||||
- fedora
|
||||
- arch
|
||||
stdin: |
|
||||
trap 'echo "[exit]"' EXIT
|
||||
coproc { trap -p EXIT; echo "coproc"; }
|
||||
cat <&${COPROC[0]}
|
||||
wait
|
||||
echo "main continues"
|
||||
|
||||
- name: "coproc - DEBUG trap inheritance"
|
||||
skip: true # TODO: this test is too inconsistent
|
||||
min_oracle_version: "5.3.0"
|
||||
incompatible_os:
|
||||
- fedora
|
||||
stdin: |
|
||||
trap 'echo "[debug]"' DEBUG
|
||||
coproc { trap -p DEBUG; echo "coproc"; }
|
||||
cat <&${COPROC[0]}
|
||||
wait
|
||||
|
||||
- name: "coproc - ERR trap with errtrace"
|
||||
skip: true # TODO: this test is too inconsistent
|
||||
min_oracle_version: "5.3.0"
|
||||
incompatible_os:
|
||||
- fedora
|
||||
- arch
|
||||
stdin: |
|
||||
set -E
|
||||
trap 'echo "[err]"' ERR
|
||||
coproc { trap -p ERR; false; echo "after false"; }
|
||||
cat <&${COPROC[0]}
|
||||
wait
|
||||
|
||||
#
|
||||
# RETURN trap - basic behavior
|
||||
#
|
||||
- name: "RETURN trap - fires on function return"
|
||||
stdin: |
|
||||
trap 'echo "[return]"' RETURN
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
}
|
||||
myfunc
|
||||
echo "after func"
|
||||
|
||||
- name: "RETURN trap - fires with return value"
|
||||
stdin: |
|
||||
trap 'echo "[return: $?]"' RETURN
|
||||
myfunc() {
|
||||
echo "in func"
|
||||
return 42
|
||||
}
|
||||
myfunc
|
||||
echo "after func"
|
||||
|
||||
- name: "RETURN trap - fires on sourced script completion"
|
||||
known_failure: true # TODO(traps): RETURN trap not firing for sourced scripts
|
||||
test_files:
|
||||
- path: "sourced.sh"
|
||||
contents: |
|
||||
echo "in sourced"
|
||||
stdin: |
|
||||
trap 'echo "[return]"' RETURN
|
||||
. ./sourced.sh
|
||||
echo "after source"
|
||||
|
||||
- name: "RETURN trap - not inherited in function without functrace"
|
||||
stdin: |
|
||||
set +T
|
||||
trap 'echo "[return]"' RETURN
|
||||
outer() {
|
||||
inner() {
|
||||
echo "in inner"
|
||||
}
|
||||
inner
|
||||
echo "after inner"
|
||||
}
|
||||
outer
|
||||
echo "done"
|
||||
|
||||
- name: "RETURN trap - inherited with functrace"
|
||||
known_failure: true # TODO(traps): RETURN trap inheritance with functrace not implemented
|
||||
stdin: |
|
||||
set -T
|
||||
trap 'echo "[return: ${FUNCNAME[0]:-main}]"' RETURN
|
||||
outer() {
|
||||
inner() {
|
||||
echo "in inner"
|
||||
}
|
||||
inner
|
||||
echo "after inner"
|
||||
}
|
||||
outer
|
||||
echo "done"
|
||||
|
||||
- name: "RETURN trap - in explicit subshell"
|
||||
stdin: |
|
||||
trap 'echo "[return]"' RETURN
|
||||
(
|
||||
myfunc() { echo "in func"; }
|
||||
myfunc
|
||||
)
|
||||
echo "after subshell"
|
||||
|
||||
- name: "RETURN trap - does not fire for regular commands"
|
||||
stdin: |
|
||||
trap 'echo "[return]"' RETURN
|
||||
echo "cmd1"
|
||||
echo "cmd2"
|
||||
true
|
||||
echo "done"
|
||||
|
||||
- name: "RETURN trap - does not fire for subshell completion"
|
||||
stdin: |
|
||||
trap 'echo "[return]"' RETURN
|
||||
(echo "in subshell")
|
||||
echo "after subshell"
|
||||
|
||||
#
|
||||
# Signal trap execution verification
|
||||
#
|
||||
- name: "trap SIGUSR1"
|
||||
known_failure: true # TODO(signals): signal handling
|
||||
stdin: |
|
||||
trap 'echo "[usr1 received]"' USR1
|
||||
trap -p USR1
|
||||
kill -USR1 $$
|
||||
echo "after signal"
|
||||
|
||||
- name: "trap SIGUSR2"
|
||||
known_failure: true # TODO(signals): signal handling
|
||||
stdin: |
|
||||
trap 'echo "[usr2 received]"' SIGUSR2
|
||||
trap -p USR2
|
||||
kill -USR2 $$
|
||||
echo "after signal"
|
||||
|
||||
- name: "trap multiple signals with same handler"
|
||||
known_failure: true # TODO(signals): signal handling
|
||||
stdin: |
|
||||
trap 'echo "[signal received]"' USR1 USR2
|
||||
kill -USR1 $$
|
||||
kill -USR2 $$
|
||||
echo "done"
|
||||
|
||||
- name: "trap handler can inspect signal"
|
||||
known_failure: true # TODO(signals): signal handling
|
||||
stdin: |
|
||||
handler() {
|
||||
echo "handler called"
|
||||
}
|
||||
trap handler USR1
|
||||
kill -USR1 $$
|
||||
echo "after"
|
||||
|
||||
#
|
||||
# Trap reset and unset
|
||||
#
|
||||
- name: "trap - clears trap"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap -p INT
|
||||
trap - INT
|
||||
trap -p INT
|
||||
|
||||
- name: "trap '' ignores signal"
|
||||
known_failure: true # TODO(signals): signal handling
|
||||
stdin: |
|
||||
trap '' INT
|
||||
trap -p INT
|
||||
kill -INT $$
|
||||
echo "survived"
|
||||
|
||||
- name: "trap with empty string can be cleared"
|
||||
stdin: |
|
||||
trap '' INT
|
||||
trap -p INT
|
||||
trap - INT
|
||||
trap -p INT
|
||||
|
||||
#
|
||||
# trap -p edge cases
|
||||
#
|
||||
- name: "trap -p - with no args shows all traps"
|
||||
stdin: |
|
||||
trap 'echo a' INT
|
||||
trap 'echo b' TERM
|
||||
trap 'echo c' EXIT
|
||||
trap -p | sort
|
||||
|
||||
- name: "trap -p - with specific signal"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap 'echo "[term]"' TERM
|
||||
trap -p INT
|
||||
trap -p TERM
|
||||
|
||||
- name: "trap -p - with multiple signals"
|
||||
stdin: |
|
||||
trap 'echo "[int]"' INT
|
||||
trap 'echo "[term]"' TERM
|
||||
trap 'echo "[usr1]"' USR1
|
||||
trap -p INT TERM USR1
|
||||
|
||||
- name: "trap -p - for unset trap shows nothing"
|
||||
stdin: |
|
||||
trap -p INT
|
||||
echo "done"
|
||||
|
||||
- name: "trap -p - output is re-executable"
|
||||
stdin: |
|
||||
trap 'echo "hello world"' INT
|
||||
eval "$(trap -p INT)"
|
||||
trap -p INT
|
||||
|
||||
#
|
||||
# Trap handler quoting
|
||||
#
|
||||
- name: "trap handler - single quotes preserved"
|
||||
known_failure: true # TODO(traps): quote escaping in trap -p output differs
|
||||
stdin: |
|
||||
trap 'echo '\''quoted'\''' INT
|
||||
trap -p INT
|
||||
|
||||
- name: "trap handler - double quotes in handler"
|
||||
stdin: |
|
||||
trap 'echo "double quoted"' INT
|
||||
trap -p INT
|
||||
|
||||
- name: "trap handler - special characters"
|
||||
stdin: |
|
||||
trap 'echo $HOME; echo "done"' INT
|
||||
trap -p INT
|
||||
|
||||
- name: "trap handler - newlines in handler"
|
||||
stdin: |
|
||||
trap 'echo line1
|
||||
echo line2' INT
|
||||
trap -p INT
|
||||
|
||||
#
|
||||
# DEBUG trap - basic behavior
|
||||
#
|
||||
- name: "DEBUG trap - sees BASH_COMMAND"
|
||||
known_failure: true # TODO(BASH_COMMAND): not showing assignment, quotes differ
|
||||
stdin: |
|
||||
trap 'echo "cmd: $BASH_COMMAND"' DEBUG
|
||||
x=1
|
||||
echo "x is $x"
|
||||
|
||||
- name: "DEBUG trap - sees LINENO"
|
||||
known_failure: true # TODO(traps): LINENO in DEBUG trap not accurate
|
||||
stdin: |
|
||||
trap 'echo "line: $LINENO"' DEBUG
|
||||
echo first
|
||||
echo second
|
||||
|
||||
- name: "DEBUG trap - fires before each simple command"
|
||||
known_failure: true # TODO(traps): DEBUG trap not firing for trap command itself
|
||||
stdin: |
|
||||
count=0
|
||||
trap '((count++))' DEBUG
|
||||
echo a
|
||||
echo b
|
||||
echo c
|
||||
echo "count: $count"
|
||||
|
||||
- name: "DEBUG trap - in loop"
|
||||
known_failure: true # TODO(BASH_COMMAND): quotes differ
|
||||
stdin: |
|
||||
trap 'echo "[debug: $BASH_COMMAND]"' DEBUG
|
||||
for i in 1 2; do
|
||||
echo "iter $i"
|
||||
done
|
||||
|
||||
#
|
||||
# EXIT trap - advanced cases
|
||||
#
|
||||
- name: "EXIT trap - has access to exit status"
|
||||
stdin: |
|
||||
trap 'echo "exiting with: $?"' EXIT
|
||||
exit 42
|
||||
|
||||
- name: "EXIT trap - runs even after errexit"
|
||||
stdin: |
|
||||
set -e
|
||||
trap 'echo "[exit trap ran]"' EXIT
|
||||
false
|
||||
echo "not reached"
|
||||
|
||||
- name: "EXIT trap - can modify exit status"
|
||||
known_failure: true # TODO(traps): EXIT trap exit doesn't modify status
|
||||
stdin: |
|
||||
trap 'exit 0' EXIT
|
||||
exit 99
|
||||
|
||||
- name: "EXIT trap - multiple assignments last one wins"
|
||||
stdin: |
|
||||
trap 'echo "first"' EXIT
|
||||
trap 'echo "second"' EXIT
|
||||
echo "done"
|
||||
|
||||
#
|
||||
# EXIT trap in -c mode
|
||||
#
|
||||
- name: "EXIT trap fires in -c mode"
|
||||
args:
|
||||
- "-c"
|
||||
- "trap 'echo exiting' EXIT; echo hello"
|
||||
expected_stdout: |
|
||||
hello
|
||||
exiting
|
||||
|
||||
- name: "EXIT trap in -c mode has access to exit status"
|
||||
args:
|
||||
- "-c"
|
||||
- "trap 'echo \"status: $?\"' EXIT; exit 42"
|
||||
expected_stdout: "status: 42\n"
|
||||
|
||||
- name: "EXIT trap in -c mode runs after errexit"
|
||||
args:
|
||||
- "-c"
|
||||
- "set -e; trap 'echo trapped' EXIT; false"
|
||||
expected_stdout: "trapped\n"
|
||||
|
||||
- name: "EXIT trap in -c mode with multiple assignments last one wins"
|
||||
args:
|
||||
- "-c"
|
||||
- "trap 'echo first' EXIT; trap 'echo second' EXIT; echo done"
|
||||
expected_stdout: |
|
||||
done
|
||||
second
|
||||
|
||||
#
|
||||
# Trap with multiple signals
|
||||
#
|
||||
- name: "trap - clear multiple signals at once"
|
||||
stdin: |
|
||||
trap 'echo "[handler]"' INT TERM USR1
|
||||
trap -p INT
|
||||
trap -p TERM
|
||||
trap -p USR1
|
||||
echo "---"
|
||||
trap - INT TERM USR1
|
||||
trap -p INT
|
||||
trap -p TERM
|
||||
trap -p USR1
|
||||
echo "done"
|
||||
|
||||
- name: "trap - ignore multiple signals"
|
||||
stdin: |
|
||||
trap '' INT TERM
|
||||
trap -p INT
|
||||
trap -p TERM
|
||||
echo "done"
|
||||
@@ -0,0 +1,7 @@
|
||||
name: "true and false"
|
||||
cases:
|
||||
- name: "true and false ignore all args"
|
||||
stdin: |
|
||||
builtin false --help; echo "builtin false --help => $?"
|
||||
builtin true --help; echo "builtin true --help => $?"
|
||||
builtin true s87tien5; echo "builtin true s87tien5 => $?"
|
||||
@@ -0,0 +1,86 @@
|
||||
name: "Builtins: type"
|
||||
cases:
|
||||
- name: "Test type with no arguments"
|
||||
stdin: type
|
||||
|
||||
- name: "Test type with a valid command"
|
||||
stdin: type ls
|
||||
|
||||
- name: "Test type with an invalid command"
|
||||
ignore_stderr: true
|
||||
stdin: type invalid_command
|
||||
|
||||
- name: "Test type with -t option and a builtin command"
|
||||
stdin: type -t cd
|
||||
|
||||
- name: "Test type with -t option and an external command"
|
||||
stdin: type -t ls
|
||||
|
||||
- name: "Test type with -t option and non-existent command"
|
||||
ignore_stderr: true
|
||||
stdin: type -t non-existent
|
||||
|
||||
- name: "Test type with -a option and non-existent command"
|
||||
ignore_stderr: true
|
||||
stdin: type -a non-existent
|
||||
|
||||
- name: "Test type with -a option and a command with multiple definitions"
|
||||
stdin: type -a true
|
||||
|
||||
- name: Test type with -p option and a builtin command
|
||||
stdin: type -p cd
|
||||
|
||||
- name: Test type with -p option and an external command
|
||||
stdin: type -p ls
|
||||
|
||||
- name: Test type with -p option and non-existent command
|
||||
stdin: type -p non-existent
|
||||
|
||||
- name: Test type with -P option and a builtin command
|
||||
stdin: type -P cd
|
||||
|
||||
- name: Test type with -P option and an external command
|
||||
stdin: type -P ls
|
||||
|
||||
- name: Test type with -P option and non-existent command
|
||||
stdin: type -P non-existent
|
||||
|
||||
- name: Test type with -f option and a function
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
function myfunc() { echo "Hello, world!"; }
|
||||
type -f myfunc
|
||||
|
||||
- name: Test type with -f option and a command
|
||||
stdin: type -f ls
|
||||
|
||||
- name: Test type with -a option and a function
|
||||
stdin: |
|
||||
function myfunc() { echo "Hello, world!"; }
|
||||
type -a myfunc
|
||||
|
||||
- name: Test type with hashed path
|
||||
stdin: |
|
||||
hash -p /some/ls ls
|
||||
type ls
|
||||
|
||||
- name: Test type -a with hashed path
|
||||
stdin: |
|
||||
hash -p /some/ls ls
|
||||
type -a ls
|
||||
|
||||
- name: Test type -P with hashed path
|
||||
stdin: |
|
||||
hash -p /some/ls ls
|
||||
type -P ls
|
||||
|
||||
- name: Test type -P -a with hashed path
|
||||
min_oracle_version: 5.3 # Behavior changed in bash 5.3
|
||||
stdin: |
|
||||
hash -p /some/ls ls
|
||||
type -P -a ls
|
||||
|
||||
- name: Test type -p -a with hashed path
|
||||
stdin: |
|
||||
hash -p /some/ls ls
|
||||
type -p -a ls
|
||||
@@ -0,0 +1,10 @@
|
||||
name: "Builtins: typeset"
|
||||
|
||||
cases:
|
||||
- name: "Display vars"
|
||||
stdin: |
|
||||
typeset myvar=something
|
||||
typeset -p myvar
|
||||
|
||||
typeset myarr=(a b c)
|
||||
typeset -p myarr
|
||||
@@ -0,0 +1,37 @@
|
||||
name: "Builtins: ulimit"
|
||||
cases:
|
||||
- name: "ulimit -c"
|
||||
stdin: |
|
||||
ulimit -c
|
||||
|
||||
- name: "ulimit -d"
|
||||
stdin: |
|
||||
ulimit -d
|
||||
|
||||
- name: "ulimit -f"
|
||||
stdin: |
|
||||
ulimit -f
|
||||
|
||||
- name: "ulimit -l"
|
||||
stdin: |
|
||||
ulimit -l
|
||||
|
||||
- name: "ulimit -m"
|
||||
stdin: |
|
||||
ulimit -m
|
||||
|
||||
- name: "ulimit -n"
|
||||
stdin: |
|
||||
ulimit -n
|
||||
|
||||
- name: "ulimit -d unlimited"
|
||||
stdin: |
|
||||
ulimit -d unlimited
|
||||
|
||||
- name: "ulimit -f unlimited"
|
||||
stdin: |
|
||||
ulimit -f unlimited
|
||||
|
||||
- name: "ulimit -m unlimited"
|
||||
stdin: |
|
||||
ulimit -m unlimited
|
||||
@@ -0,0 +1,15 @@
|
||||
name: "Builtins: unalias"
|
||||
cases:
|
||||
- name: "Unalias basic usage"
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
alias echo='echo prefixed'
|
||||
echo 'something'
|
||||
unalias echo
|
||||
echo 'something'
|
||||
|
||||
- name: "Unalias non-existent alias"
|
||||
ignore_stderr: true # Slightly different error messages
|
||||
stdin: |
|
||||
shopt -s expand_aliases
|
||||
unalias not_an_alias
|
||||
@@ -0,0 +1,261 @@
|
||||
name: "Builtins: unset"
|
||||
cases:
|
||||
- name: "Basic unset usage"
|
||||
stdin: |
|
||||
var=value
|
||||
unset var
|
||||
echo "var: ${var}"
|
||||
|
||||
- name: "Unset multiple variables"
|
||||
stdin: |
|
||||
var1=value1
|
||||
var2=value2
|
||||
unset var1 var2
|
||||
echo "var1: ${var1}, var2: ${var2}"
|
||||
|
||||
- name: "Unset function"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
function myfunc() {
|
||||
echo "Hello, world!"
|
||||
}
|
||||
unset myfunc
|
||||
myfunc
|
||||
|
||||
- name: "Unset -f function"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
function myfunc() {
|
||||
echo "Hello, world!"
|
||||
}
|
||||
unset -f myfunc
|
||||
myfunc
|
||||
|
||||
- name: "Unset indexed array element"
|
||||
stdin: |
|
||||
declare -a myarray=(a b c)
|
||||
|
||||
unset myarray[0]
|
||||
declare -p myarray
|
||||
|
||||
unset myarray[3]
|
||||
declare -p myarray
|
||||
|
||||
unset myarray[2]
|
||||
declare -p myarray
|
||||
|
||||
unset myarray[1]
|
||||
declare -p myarray
|
||||
|
||||
- name: "Unset associative array element"
|
||||
stdin: |
|
||||
echo "[1]"
|
||||
|
||||
declare -A myarray=([key]=value)
|
||||
|
||||
unset myarray[key]
|
||||
declare -p myarray
|
||||
|
||||
echo "[2]"
|
||||
|
||||
declare -A myarray=([key]=value)
|
||||
|
||||
unset "myarray[key]"
|
||||
declare -p myarray
|
||||
|
||||
echo "[3]"
|
||||
|
||||
declare -A myarray=([key]=value)
|
||||
key="x:y:z"
|
||||
|
||||
unset "myarray[key]"
|
||||
declare -p myarray
|
||||
|
||||
- name: "Unset array element with interesting expression"
|
||||
stdin: |
|
||||
declare -a myarray=(a b c d e)
|
||||
|
||||
echo "Initial array:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
i=4
|
||||
unset myarray[i]
|
||||
echo "After removing element at index 4:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
unset myarray[6/2]
|
||||
echo "After removing element at index 3:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
- name: "Unset array element with negative index"
|
||||
stdin: |
|
||||
declare -a myarray=(a b c d e)
|
||||
|
||||
echo "Initial array:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
unset "myarray[-1]"
|
||||
echo "After removing last element (index -1):"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
unset "myarray[-1]"
|
||||
echo "After removing new last element (index -1 again):"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
unset "myarray[-2]"
|
||||
echo "After removing second-to-last element (index -2):"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
- name: "Unset array element with negative index out of bounds"
|
||||
ignore_stderr: true
|
||||
stdin: |
|
||||
declare -a myarray=(a b c)
|
||||
|
||||
echo "Initial array:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
unset "myarray[-4]"
|
||||
echo "After attempting to remove with index -4 (out of bounds):"
|
||||
declare -p myarray
|
||||
echo "Return code: $?"
|
||||
|
||||
- name: "Unset array element with variable negative index"
|
||||
stdin: |
|
||||
declare -a myarray=(10 20 30 40 50)
|
||||
|
||||
echo "Initial array:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
i=-1
|
||||
unset "myarray[i]"
|
||||
echo "After removing with variable index i=-1:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
i=-2
|
||||
unset "myarray[i]"
|
||||
echo "After removing with variable index i=-2:"
|
||||
declare -p myarray
|
||||
echo
|
||||
|
||||
- name: "Unset local in same function"
|
||||
stdin: |
|
||||
var="global"
|
||||
|
||||
myfunc() {
|
||||
echo "In myfunc"
|
||||
local -i var="10"
|
||||
declare -p var
|
||||
unset var
|
||||
echo "After unset"
|
||||
declare -p var
|
||||
var="20"
|
||||
}
|
||||
|
||||
echo "Before call: var=${var}"
|
||||
myfunc
|
||||
echo "After call: var=${var}"
|
||||
|
||||
- name: "Unset locals in callers"
|
||||
stdin: |
|
||||
firstfunc() {
|
||||
local var="first"
|
||||
echo "entered firstfunc: var=${var}"
|
||||
secondfunc
|
||||
echo "leaving firstfunc: var=${var}"
|
||||
}
|
||||
|
||||
secondfunc() {
|
||||
local var="second"
|
||||
echo "entered secondfunc: var=${var}"
|
||||
thirdfunc
|
||||
echo "leaving secondfunc: var=${var}"
|
||||
}
|
||||
|
||||
thirdfunc() {
|
||||
echo "entered thirdfunc: var=${var}"
|
||||
unset -v var
|
||||
echo "after first unset: var=${var}"
|
||||
var+=":updated"
|
||||
echo " ...and updated: var=${var}"
|
||||
unset -v var
|
||||
echo "after second unset: var=${var}"
|
||||
var+=":updated"
|
||||
echo " ...and updated: var=${var}"
|
||||
unset -v var
|
||||
echo "after third unset: var=${var}"
|
||||
var+=":updated"
|
||||
echo " ...and updated: var=${var}"
|
||||
echo "leaving firstfunc: var=${var}"
|
||||
}
|
||||
|
||||
var="global"
|
||||
echo "before calls: var=${var}"
|
||||
firstfunc
|
||||
echo "after calls: var=${var}"
|
||||
|
||||
- name: "Unset with nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
declare -n ref=var
|
||||
var="value"
|
||||
|
||||
echo "[Before unset]"
|
||||
echo "ref: ${ref}"
|
||||
echo "var: ${var}"
|
||||
|
||||
unset ref
|
||||
|
||||
echo "[After unset]"
|
||||
echo "ref: ${ref}"
|
||||
echo "var: ${var}"
|
||||
|
||||
- name: "Unset -n removes nameref itself"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target="value"
|
||||
declare -n ref=target
|
||||
echo "before: target=$target ref=$ref"
|
||||
unset -n ref
|
||||
echo "after unset -n: target=$target"
|
||||
declare -p ref 2>/dev/null || echo "ref is gone"
|
||||
declare -p target 2>/dev/null && echo "target still declared"
|
||||
|
||||
- name: "Unset array element through nameref"
|
||||
known_failure: true
|
||||
stdin: |
|
||||
target=(a b c d e)
|
||||
declare -n ref=target
|
||||
unset 'ref[2]'
|
||||
declare -p target
|
||||
unset 'ref[0]'
|
||||
declare -p target
|
||||
|
||||
- name: "Unset odd function names"
|
||||
stdin: |
|
||||
echo "[Checking existing]"
|
||||
type [
|
||||
|
||||
echo "[Defining function]"
|
||||
function [() {
|
||||
:
|
||||
}
|
||||
|
||||
echo "[Checking defined]"
|
||||
type [
|
||||
|
||||
echo "[Unsetting function]"
|
||||
unset [
|
||||
echo "unset result: $?"
|
||||
|
||||
echo "[Checking after unset]"
|
||||
type [
|
||||
@@ -0,0 +1,52 @@
|
||||
name: "Builtins: wait"
|
||||
cases:
|
||||
- name: "wait for specific failed PID"
|
||||
known_failure: true # TODO(jobs): wait should return job's exit status
|
||||
stdin: |
|
||||
(exit 5) &
|
||||
pid=$!
|
||||
wait $pid
|
||||
echo "status: $?"
|
||||
|
||||
- name: "wait for successful PID"
|
||||
stdin: |
|
||||
(exit 0) &
|
||||
pid=$!
|
||||
wait $pid
|
||||
echo "status: $?"
|
||||
|
||||
- name: "wait in conditional for failed PID"
|
||||
known_failure: true # TODO(jobs): wait should return job's exit status
|
||||
stdin: |
|
||||
(exit 5) &
|
||||
pid=$!
|
||||
if wait $pid; then
|
||||
echo "then"
|
||||
else
|
||||
echo "else"
|
||||
fi
|
||||
|
||||
- name: "wait with no arguments"
|
||||
stdin: |
|
||||
(exit 0) &
|
||||
(exit 0) &
|
||||
wait
|
||||
echo "after wait all"
|
||||
|
||||
- name: "wait -n not implemented"
|
||||
known_failure: true # TODO(builtins): wait -n not implemented
|
||||
stdin: |
|
||||
(exit 0) &
|
||||
(exit 5) &
|
||||
wait -n
|
||||
echo "first done, status: $?"
|
||||
wait -n
|
||||
echo "second done, status: $?"
|
||||
|
||||
- name: "Background job failure with wait"
|
||||
known_failure: true # TODO(jobs): wait should return job's exit status
|
||||
stdin: |
|
||||
{ sleep 0.01; exit 3; } &
|
||||
pid=$!
|
||||
wait $pid
|
||||
echo "status: $?"
|
||||
Reference in New Issue
Block a user