310 lines
7.9 KiB
YAML
310 lines
7.9 KiB
YAML
name: "Special parameters"
|
|
cases:
|
|
- name: "$_ - initial value matches $0"
|
|
stdin: |
|
|
[[ "$0" == "$_" ]] && echo "matches on start"
|
|
|
|
- name: "$_ - last argument tracked across simple commands"
|
|
stdin: |
|
|
: one
|
|
echo "[$_]"
|
|
: two three four
|
|
echo "[$_]"
|
|
echo last_echo_arg
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - zero-arg command uses command name"
|
|
stdin: |
|
|
echo seed1 seed2
|
|
:
|
|
echo "after colon: [$_]"
|
|
true
|
|
echo "after true: [$_]"
|
|
echo
|
|
echo "after bare echo: [$_]"
|
|
|
|
- name: "$_ - args with spaces and special chars"
|
|
stdin: |
|
|
echo "seed last"
|
|
echo "[$_]"
|
|
echo "arg with spaces" 'arg-with-dashes' "arg:with:colons"
|
|
echo "[$_]"
|
|
echo "" trailing
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - builtin dispatch path"
|
|
stdin: |
|
|
echo seed last
|
|
: bu1 bu2 bu3
|
|
echo "[$_]"
|
|
printf '%s\n' pf1 pf2
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - declaration assignment uses expanded last argument"
|
|
stdin: |
|
|
value=world
|
|
declare declared=$value
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - wrapper assignments use expanded last argument"
|
|
stdin: |
|
|
value=world
|
|
builtin echo builtin=$value
|
|
echo "builtin: [$_]"
|
|
command echo command=$value
|
|
echo "command: [$_]"
|
|
|
|
- name: "$_ - external command dispatch path"
|
|
stdin: |
|
|
echo seed last
|
|
$(which true) ext1 ext2
|
|
echo "[$_]"
|
|
env ENV=1 $(which true) external_final
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - external command exports resolved path via env"
|
|
stdin: |
|
|
# When invoking an external command, it should see _ set
|
|
# to the resolved pathname of the command (not "last arg").
|
|
$(which env) | grep '^_='
|
|
|
|
- name: "$_ - function dispatch sets to call-site last arg"
|
|
stdin: |
|
|
f() { :; }
|
|
echo seed_last
|
|
f call_a call_b
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - function body sees pre-invocation value on entry"
|
|
stdin: |
|
|
f() {
|
|
echo "entry: [$_]"
|
|
echo new_inner
|
|
echo "after inner echo: [$_]"
|
|
}
|
|
echo pre_seed
|
|
f call_arg
|
|
echo "after return: [$_]"
|
|
|
|
- name: "$_ - nested function dispatch"
|
|
stdin: |
|
|
inner() { echo innerdone; }
|
|
outer() { inner ia ib; }
|
|
echo seed
|
|
outer oa ob
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - assignment-only function body"
|
|
stdin: |
|
|
f() { a=1; b=2; }
|
|
echo seed_last
|
|
f foo bar
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - cleared by assignment-only statements"
|
|
stdin: |
|
|
echo seed_last
|
|
x=assigned
|
|
echo "after assign: [$_]"
|
|
y=another
|
|
echo "after second assign: [$_]"
|
|
: colonarg
|
|
echo "after colon: [$_]"
|
|
|
|
- name: "$_ - cleared by assignment with command substitution"
|
|
stdin: |
|
|
echo seed_last
|
|
z=$(echo subst_inner)
|
|
echo "after assignment: [$_]"
|
|
|
|
- name: "$_ - single-stage pipeline does not update parent"
|
|
stdin: |
|
|
echo prev_a prev_b
|
|
echo pipe_a pipe_b | cat
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - multi-stage pipeline does not update parent"
|
|
stdin: |
|
|
echo prev_a prev_b
|
|
echo pipe_a pipe_b | cat | cat
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - and-chain tracks last arg of last executed command"
|
|
stdin: |
|
|
echo seed_last
|
|
true && echo a1 a2
|
|
echo "[$_]"
|
|
true && true t1 t2
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - or-chain tracks last arg of last executed command"
|
|
stdin: |
|
|
echo seed_last
|
|
false || echo or1 or2
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - command-not-found update"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo seed_last
|
|
nosuchcmd_xyzzy fail_a fail_b
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - for loop body"
|
|
stdin: |
|
|
echo seed_last
|
|
for i in 1 2 3; do :; done
|
|
echo "[$_]"
|
|
for i in a b c; do echo "loop $i"; done
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - while loop body"
|
|
stdin: |
|
|
echo seed_last
|
|
i=0
|
|
while [ $i -lt 2 ]; do echo "iter $i"; i=$((i+1)); done
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - until loop body"
|
|
stdin: |
|
|
echo seed_last
|
|
i=0
|
|
until [ $i -ge 2 ]; do i=$((i+1)); done
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - if/then body"
|
|
stdin: |
|
|
echo seed_last
|
|
if true; then echo then_a then_b; fi
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - case body"
|
|
stdin: |
|
|
echo seed_last
|
|
case x in
|
|
x) echo match_a match_b ;;
|
|
esac
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - eval tracks eval's last arg"
|
|
stdin: |
|
|
echo seed_last
|
|
eval "echo evaled one two"
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - source tracks sourced script's commands"
|
|
test_files:
|
|
- path: sourced.sh
|
|
contents: |
|
|
echo sourced_a sourced_b
|
|
stdin: |
|
|
echo seed_last
|
|
. ./sourced.sh
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - subshell does not leak into parent"
|
|
stdin: |
|
|
echo outer_a outer_b
|
|
(echo inner1 inner2; echo "sub: [$_]")
|
|
echo "parent: [$_]"
|
|
|
|
- name: "$_ - background job + wait"
|
|
stdin: |
|
|
echo seed_last
|
|
(:) &
|
|
wait
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - exported attribute after update"
|
|
stdin: |
|
|
export _=initial_value
|
|
echo first last
|
|
# After commands have run, does _ still have the -x attribute?
|
|
declare -p _ | grep -c '^declare -x'
|
|
|
|
- name: "$_ - declare -p reflects updated value"
|
|
stdin: |
|
|
export _=initial_value
|
|
echo first last
|
|
declare -p _
|
|
|
|
- name: "$_ - readonly is honored"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
readonly _=frozen
|
|
echo hi there
|
|
echo "[$_]"
|
|
|
|
- name: "$_ - exec does not override _ in child env"
|
|
known_failure: true # TODO(exec): exec sets _ to the program's path, as for any external command
|
|
stdin: |
|
|
# bash does not set _ in the child's env for exec; the child inherits
|
|
# the parent's _ value.
|
|
(exec $(which env)) | grep '^_='
|
|
|
|
- name: "$_ - exec at the top level does not override _ in child env"
|
|
stdin: |
|
|
# Unlike in a subshell, exec here replaces the shell itself. What _ the child
|
|
# inherits differs between shells (it's the shell's own path), so check only
|
|
# that exec didn't set it to the program's.
|
|
printenv_path=$(which printenv)
|
|
underscore=$("$0" -c 'exec "$(which printenv)" _')
|
|
[[ "${underscore}" == "${printenv_path}" ]] && echo "exec set _" || echo "exec left _ alone"
|
|
|
|
- name: "$_ - exec passes on the _ the shell inherited"
|
|
stdin: |
|
|
# The shell running "$0" sets its _ to the path it ran it by; exec passes that on.
|
|
underscore=$("$0" -c 'exec printenv _')
|
|
[[ "${underscore}" == "$0" ]] && echo "inherited _" || echo "other _: ${underscore}"
|
|
|
|
- name: "$_ - without one to inherit, a shell starts with an unexported $0"
|
|
stdin: |
|
|
env -u _ "$0" -c '[[ $_ == "$0" ]] && echo "_ is \$0"; exec printenv _'
|
|
echo "printenv: $?"
|
|
|
|
- name: "$_ - exec after a command passes no _"
|
|
stdin: |
|
|
# Updating $_ unexports it, so a command run before exec leaves none to pass on.
|
|
"$0" -c 'true; exec printenv _'
|
|
echo "printenv: $?"
|
|
|
|
- name: "$@ and $*"
|
|
stdin: |
|
|
function myfunc() {
|
|
echo "--myfunc called--"
|
|
|
|
echo "COUNT: $#"
|
|
echo "AT-SIGN VALUE: '$@'"
|
|
echo "STAR VALUE: '$*'"
|
|
|
|
for arg in $@; do
|
|
echo "AT-SIGN ELEMENT: '${arg}'"
|
|
done
|
|
|
|
for arg in $*; do
|
|
echo "STAR ELEMENT: '${arg}'"
|
|
done
|
|
|
|
for arg in "$@"; do
|
|
echo "DOUBLE-QUOTED AT-SIGN ELEMENT: '${arg}'"
|
|
done
|
|
|
|
for arg in "$*"; do
|
|
echo "DOUBLE-QUOTED STAR ELEMENT: '${arg}'"
|
|
done
|
|
}
|
|
|
|
myfunc
|
|
myfunc 1
|
|
myfunc 1 2
|
|
myfunc "a b c" 2
|
|
|
|
- name: "$_ - command and builtin query forms"
|
|
stdin: |
|
|
echo seed > /dev/null
|
|
command -v echo > /dev/null; printf 'command -v: [%s]\n' "$_"
|
|
echo seed > /dev/null
|
|
builtin > /dev/null; printf 'builtin bare: [%s]\n' "$_"
|
|
echo seed > /dev/null
|
|
command > /dev/null; printf 'command bare: [%s]\n' "$_"
|