484 lines
14 KiB
YAML
484 lines
14 KiB
YAML
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"
|
|
# On NixOS, `command -p`'s default "standard utilities" path diverges between
|
|
# brush and the oracle. brush derives it from glibc's confstr(_CS_PATH), which
|
|
# nixpkgs patches to include the Nix profile (so `cat` is found there), whereas
|
|
# nixpkgs' bash resolves a default path that excludes the Nix profile (so it
|
|
# finds nothing). This reflects where NixOS keeps its standard utilities, not a
|
|
# brush behavior difference, so it can't be reconciled against the oracle.
|
|
incompatible_os: ["nixos"]
|
|
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 with multiple operands"
|
|
stdin: |
|
|
echo "[resolved builtins]"
|
|
command -v echo true pwd
|
|
echo "rc=$?"
|
|
|
|
echo "[skipped miss]"
|
|
command -v echo non-existent pwd
|
|
echo "rc=$?"
|
|
|
|
echo "[all misses]"
|
|
command -v non-existent also-non-existent
|
|
echo "rc=$?"
|
|
|
|
- name: "command -V with multiple operands"
|
|
ignore_stderr: true # miss wording on stderr differs between brush and bash
|
|
stdin: |
|
|
command -V echo true
|
|
echo "rc=$?"
|
|
|
|
command -V non-existent || echo "not found"
|
|
|
|
- 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 forwards expanded declaration assignments"
|
|
known_failure: true # TODO: command builtin improvements required
|
|
stdin: |
|
|
value=world
|
|
command declare declared=$value
|
|
command export exported=$value
|
|
declare -p declared exported
|
|
|
|
- name: "nested wrappers resolve ordinary and declaration targets"
|
|
known_failure: true # TODO: command builtin improvements required
|
|
stdin: |
|
|
value=world
|
|
command builtin echo ordinary=$value
|
|
command builtin declare declared=$value
|
|
builtin command echo reverse=$value
|
|
declare -p declared
|
|
|
|
- name: "command expands assignment-looking argument for ordinary builtin"
|
|
stdin: |
|
|
value=world
|
|
command echo argument=$value
|
|
|
|
- name: "command expands assignment-looking argument for external command"
|
|
stdin: |
|
|
value=world
|
|
command sh -c 'printf "<%s>\n" "$1"' sh argument=$value
|
|
|
|
- name: "command expands assignment-looking command name"
|
|
stdin: |
|
|
value=world
|
|
command missing=$value 2>/dev/null
|
|
printf 'status=%s last=<%s>\n' "$?" "$_"
|
|
|
|
- name: "command with -p"
|
|
ignore_stderr: true # In case it fails on the oracle as well, ignore the specific error message.
|
|
# See "command -v -p" above: `command -p`'s default utility path resolves
|
|
# differently on NixOS for brush (glibc confstr) vs. the oracle (nixpkgs bash).
|
|
incompatible_os: ["nixos"]
|
|
stdin: |
|
|
unset PATH
|
|
|
|
command -p -- ls
|
|
|
|
#
|
|
# Regression coverage: `command` does not change whether an argument is an assignment word, so
|
|
# its arguments undergo ordinary word expansion and the nested builtin recognizes any assignment
|
|
# syntax that survives.
|
|
#
|
|
- name: "command does not preserve assignment splitting semantics"
|
|
known_failure: true # TODO: command builtin improvements required
|
|
ignore_stderr: true
|
|
stdin: |
|
|
v="x y"
|
|
command declare b=$v
|
|
declare -p b y
|
|
|
|
- name: "command forwards a value with no field separators intact"
|
|
known_failure: true # TODO: command builtin improvements required
|
|
stdin: |
|
|
value=world
|
|
command declare declared=$value
|
|
command readonly fixed=$value
|
|
declare -p declared fixed
|
|
|
|
- name: "Function shadowing command receives its arguments"
|
|
stdin: |
|
|
command() { echo "shadow command: [$*] count=$#"; }
|
|
command echo hi
|
|
unset -f command
|
|
|
|
- name: "command -v finds aliases, keywords, and functions"
|
|
stdin: |
|
|
shopt -s expand_aliases
|
|
alias al="echo hi"
|
|
f() { :; }
|
|
|
|
command -v al; echo "rc=$?"
|
|
command -v if; echo "rc=$?"
|
|
command -v f; echo "rc=$?"
|
|
command -v echo; echo "rc=$?"
|
|
|
|
- name: "command -V describes aliases, keywords, and functions"
|
|
stdin: |
|
|
shopt -s expand_aliases
|
|
alias al="echo hi"
|
|
f() { :; }
|
|
|
|
command -V al; echo "rc=$?"
|
|
command -V if; echo "rc=$?"
|
|
command -V f; echo "rc=$?"
|
|
command -V echo; echo "rc=$?"
|
|
|
|
- name: "command -v/-V with multiple names"
|
|
stdin: |
|
|
# Normalize away the leading "shell: line N:" that the oracle prefixes onto diagnostics.
|
|
strip() { sed 's/^.*command:/command:/'; }
|
|
|
|
shopt -s expand_aliases
|
|
alias al="echo hi"
|
|
f() { :; }
|
|
|
|
echo "[all found]"
|
|
command -v al f if echo; echo "rc=$?"
|
|
command -V al f if echo; echo "rc=$?"
|
|
|
|
echo "[some found]"
|
|
command -v nonexistent f 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
command -v f nonexistent 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
command -V f nonexistent 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
echo "[none found]"
|
|
command -v nope1 nope2 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
command -V nope1 nope2 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
- name: "command -v/-V output is reusable/quoted for odd alias values"
|
|
stdin: |
|
|
shopt -s expand_aliases
|
|
alias q="echo 'it'\''s"
|
|
alias q2='a "b" $c'
|
|
alias q3="'"
|
|
alias q4="a''b"
|
|
|
|
command -v q
|
|
command -v q2
|
|
command -v q3
|
|
command -v q4
|
|
command -V q
|
|
|
|
- name: "command -v/-V honors expand_aliases"
|
|
stdin: |
|
|
strip() { sed 's/^.*command:/command:/'; }
|
|
alias za="ls"
|
|
|
|
shopt -u expand_aliases
|
|
command -v za; echo "rc=$?"
|
|
command -V za 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
shopt -s expand_aliases
|
|
command -v za; echo "rc=$?"
|
|
|
|
- name: "command -v/-V: last of -v/-V wins"
|
|
stdin: |
|
|
command -v -V echo
|
|
command -V -v echo
|
|
command -vV echo
|
|
command -Vv echo
|
|
command -V -v nonexistent; echo "rc=$?"
|
|
|
|
- name: "command options given more than once"
|
|
stdin: |
|
|
command -vv echo; echo "rc=$?"
|
|
command -v -v echo; echo "rc=$?"
|
|
command -pp echo hi; echo "rc=$?"
|
|
command -vV -v echo; echo "rc=$?"
|
|
|
|
- name: "command with an unknown option fails with status 2"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
command -Z echo hi; echo "rc=$?"
|
|
command -v -Z echo; echo "rc=$?"
|
|
command --v echo; echo "rc=$?"
|
|
|
|
- name: "command passes options after the command name on"
|
|
stdin: |
|
|
command echo -v x
|
|
command printf '%s\n' -v
|
|
command -v echo -v; echo "rc=$?"
|
|
command -v -- echo; echo "rc=$?"
|
|
|
|
- name: "command -V reports hashed commands"
|
|
stdin: |
|
|
# Normalize away the temporary directory the test runs in.
|
|
scrub() { sed -E "s|$PWD|<TMPDIR>|g"; }
|
|
|
|
mkdir -p bin
|
|
printf '#!/bin/sh\n:\n' > bin/mytool
|
|
chmod +x bin/mytool
|
|
PATH="$PWD/bin:$PATH"
|
|
|
|
hash mytool
|
|
command -v mytool | scrub
|
|
command -V mytool | scrub
|
|
|
|
- name: "command -v/-V report path names as given"
|
|
stdin: |
|
|
printf '#!/bin/sh\necho hi\n' > prog
|
|
chmod +x prog
|
|
|
|
command -v ./prog; echo "rc=$?"
|
|
command -V ./prog; echo "rc=$?"
|
|
|
|
- name: "command -v with non-executable and non-file path names"
|
|
stdin: |
|
|
strip() { sed 's/^.*command:/command:/'; }
|
|
mkdir subdir
|
|
touch plainfile
|
|
|
|
command -v ./subdir; echo "rc=$?"
|
|
command -V ./subdir 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
command -v ./plainfile; echo "rc=$?"
|
|
|
|
- name: "command -v with empty name"
|
|
stdin: |
|
|
strip() { sed 's/^.*command:/command:/'; }
|
|
|
|
command -v ""; echo "rc=$?"
|
|
command -V "" 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
- name: "command -p searches the default path"
|
|
# See "command -v -p" above: on NixOS the oracle's default utility path finds no
|
|
# standard utilities at all, so the `sh` lookup below can't be reconciled there.
|
|
incompatible_os: ["nixos"]
|
|
stdin: |
|
|
strip() { sed 's/^.*command:/command:/'; }
|
|
|
|
echo "[builtins are still found]"
|
|
command -p -v echo; echo "rc=$?"
|
|
command -p -V echo; echo "rc=$?"
|
|
|
|
echo "[externals come from the default path]"
|
|
# bash's default path is compiled in and brush asks the C library for it, so the two
|
|
# can name different directories on the same host; only the basename is comparable.
|
|
dirless() { sed 's#/[^ ]*/#<dir>/#'; }
|
|
command -p -v sh | dirless; echo "rc=${PIPESTATUS[0]}"
|
|
command -p -V sh | dirless; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
echo "[PATH is ignored]"
|
|
mkdir -p bin
|
|
printf '#!/bin/sh\n:\n' > bin/mytool
|
|
chmod +x bin/mytool
|
|
PATH="$PWD/bin:$PATH"
|
|
command -v mytool > /dev/null; echo "rc=$?"
|
|
command -p -v mytool; echo "rc=$?"
|
|
|
|
echo "[missing name]"
|
|
command -p -V definitely-not-a-real-command 2>&1 | strip; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
- name: "command -V reports a PATH-found command with an absolute path"
|
|
stdin: |
|
|
# Normalize away the temporary directory the test runs in.
|
|
scrub() { sed -E "s|$PWD|<TMPDIR>|g"; }
|
|
|
|
mkdir -p d1
|
|
printf '#!/bin/sh\n:\n' > d1/ex
|
|
chmod +x d1/ex
|
|
touch d1/only
|
|
PATH="d1:$PATH"
|
|
|
|
echo "[a non-executable in PATH is still reported]"
|
|
command -v only; echo "rc=$?"
|
|
command -V only | scrub; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
command -v ex; echo "rc=$?"
|
|
command -V ex | scrub; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
echo "[a \".\" entry contributes no \"./\" to the absolute path]"
|
|
printf '#!/bin/sh\n:\n' > here; chmod +x here
|
|
PATH=".:$PATH" command -v here; echo "rc=$?"
|
|
PATH=".:$PATH" command -V here | scrub; echo "rc=${PIPESTATUS[0]}"
|
|
|
|
echo "[...but explicit and hashed paths are left alone]"
|
|
printf '#!/bin/sh\n:\n' > prog; chmod +x prog
|
|
command -V ./prog; echo "rc=$?"
|
|
hash -p rel/path hp
|
|
command -V hp; echo "rc=$?"
|
|
|
|
- name: "command -v/-V with no operands"
|
|
stdin: |
|
|
command -v; echo "rc=$?"
|
|
command -V; echo "rc=$?"
|
|
|
|
- name: "command -v/-V skips a disabled builtin"
|
|
stdin: |
|
|
# Normalize away the temporary directory the test runs in.
|
|
scrub() { sed -E "s|$PWD|<TMPDIR>|g"; }
|
|
|
|
mkdir -p bin
|
|
printf '#!/bin/sh\n:\n' > bin/echo
|
|
chmod +x bin/echo
|
|
PATH="$PWD/bin:$PATH"
|
|
|
|
# `echo` is disabled for the rest of this block, so report through printf.
|
|
enable -n echo
|
|
command -v echo | scrub; disabled_v=${PIPESTATUS[0]}
|
|
command -V echo | scrub; disabled_V=${PIPESTATUS[0]}
|
|
enable echo
|
|
printf 'rc=%s rc=%s\n' "$disabled_v" "$disabled_V"
|
|
|
|
command -v echo; echo "rc=$?"
|
|
|
|
- name: "command -v/-V reports an alias ahead of a same-named function"
|
|
stdin: |
|
|
f() { :; }
|
|
shopt -s expand_aliases
|
|
alias f='echo A'
|
|
|
|
command -v f; echo "rc=$?"
|
|
command -V f; echo "rc=$?"
|
|
|
|
- name: "command -p -v/-V still honors the hash table"
|
|
stdin: |
|
|
hash -p /nonexistent/cat cat
|
|
command -v cat; echo "rc=$?"
|
|
command -p -v cat; echo "rc=$?"
|
|
command -p -V cat; echo "rc=$?"
|
|
|
|
- name: "command passes on declaration expansion in POSIX mode"
|
|
known_failure: true # TODO(command): in POSIX mode, command doesn't expand c=$x as the declaration utility after it would
|
|
stdin: |
|
|
set -o posix
|
|
v="x y"
|
|
command declare b=$v
|
|
command command declare c=$v
|
|
declare -p b c
|
|
|
|
- name: "command passes on declaration expansion only to the next word"
|
|
known_failure: true # TODO(command): in POSIX mode, command doesn't expand c=$x as the declaration utility after it would
|
|
stdin: |
|
|
set -o posix
|
|
v="x y"
|
|
command -p declare d=$v
|
|
command -- declare e=$v
|
|
declare -p d e
|
|
|
|
- name: "command -v runs nothing, so expands no declarations"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
v="x y"
|
|
command -v printf b=$v; echo "rc=$?"
|
|
|
|
- name: "command drops a special builtin's fatal errors in POSIX mode"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
command export --bogus
|
|
echo "still running: $?"
|
|
|
|
|
|
- name: "command reports a forwarded builtin's errors as the builtin's own"
|
|
known_failure: true # TODO(command): a forwarded builtin's errors are attributed to command too
|
|
stdin: |
|
|
strip() { sed -E 's/\x1b\[[0-9;]*m//g; s/^[^ ]*: line [0-9]+: //; s/^error: //'; }
|
|
{ command export -Z; } 2>&1 | head -n 1 | strip
|
|
|
|
- name: "command's program that stops becomes a job"
|
|
known_failure: true # TODO(signals): a program that stops isn't noticed, nor made a job
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -m
|
|
command sh -c 'kill -STOP $$; echo resumed'
|
|
echo "command: $?"
|
|
jobs >jobs.txt; echo "named: $(grep -c 'command sh -c' jobs.txt)"
|
|
fg >/dev/null; echo "fg: $?"
|
|
rm jobs.txt
|
|
|
|
- name: "command drops a special builtin's special properties for the errors it returns"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
set -o posix
|
|
command . /nonexistent
|
|
echo "after .: $?"
|
|
command source /nonexistent
|
|
echo "after source: $?"
|
|
command trap "echo" BOGUS
|
|
echo "after trap: $?"
|
|
|
|
- name: "command passes --help on to the builtin it runs"
|
|
stdin: |
|
|
command cd --help >/dev/null 2>&1; echo "rc=$?"
|
|
[[ "$(command cd --help; echo .)" == "$(help cd; echo .)" ]] && echo "same as help cd"
|
|
|