Bash Arguments
From Typically At-Symbol (@) Is Better Than Star (*) for argument passing in Shell
Go to text →
TLDR: Default to "${@}"
for argument passing between functions in shell.
Content
Typically "${@}"
is what you want when you want to pass arguments from one bash function to another.
@
symbol retains the separation between arguments while *
puts them together.
Example code
foo() {
local num_args="$#"
echo "Number of arguments: $num_args"
echo "arg1: ${1:-<empty>}"
echo "arg2: ${2:-<empty>}"
echo "arg3: ${3:-<empty>}"
}
# shellcheck disable=SC2016
main() {
echo.bold '"${@}"'
foo "${@}"
echo.bold '"${*}"'
foo "${*}"
}
main "${@}" || exit 1
When we run this with separate arguments we see that "${*}"
has combined arguments into 1.
❯sr 1 2 3
"${@}"
Number of arguments: 3
arg1: 1
arg2: 2
arg3: 3
"${*}"
Number of arguments: 1
arg1: 1 2 3
arg2: <empty>
arg3: <empty>
Related
From Don't use unquoted star [${*}, $*]
Go to text →
This rule is part of SC2048 · koalaman/shellcheck Wiki
Using unquoted star $*
in bash to pass arguments easily leads to arguments being incorrectly split up based on white space, even when quotes are used.
Here is an example, where the quoted argument "1a 1b" gets split up into separate arguments.
foo() {
local num_args="$#"
echo "Number of arguments: $num_args"
echo "arg1: ${1:-<empty>}"
echo "arg2: ${2:-<empty>}"
echo "arg3: ${3:-<empty>}"
}
# shellcheck disable=SC2016
main() {
echo.bold '"${@}"'
foo "${@}"
echo.bold '"${*}"'
foo ${*}
}
main "${@}" || exit 1
Output running the above:
❯sr "1a 1b" 2
"${@}"
Number of arguments: 2
arg1: 1a 1b
arg2: 2
arg3: <empty>
"${*}"
Number of arguments: 3
arg1: 1a
arg2: 1b
arg3: 2
From Pipe & Arguments
Go to text →
Example processing pipe line by line
Test case
foo() {
if [ -t 0 ]; then
echo "Called normally"
echo "Received arg: $1"
else
echo.green "Called from a pipeline"
while IFS= read -r line; do
echo "Processing piped input: $line"
done
fi
}
export -f foo
main() {
echo "Calling foo() normally"
foo "bar"
echo "--------------------------------------------------------------------------------"
echo "Calling foo() from a pipeline"
echo "bar" | foo
}
main "${@}" || exit 1
Output:
Calling foo() normally
Called normally
Received arg: bar
--------------------------------------------------------------------------------
Calling foo() from a pipeline
Called from a pipeline
Processing piped input: bar
real 0m0.016s
user 0m0.013s
sys 0m0.004s
Compress piped input with JQ
# shellcheck disable=SC2120
bar(){
if [ -t 0 ]; then
echo "BAR Called normally: $*"
else
echo "BAR Called from a pipeline"
while IFS= read -r line; do
echo.bold "BAR Processing piped input: $line"
sleep 2
done
fi
}
foo() {
if [ -t 0 ]; then
echo "${*:?}" | jq -c | bar
else
jq -c | bar
fi
}
export -f foo
main() {
echo.green "$(date.now)"
echo.jsonl | jq | foo
echo.green "$(date.now)"
foo '{"hi":"there"}'
}
main "${@}" || exit 1
Output:
2023-12-14T21-17-51PST
BAR Called from a pipeline
BAR Processing piped input: {"title":"title-val-1","note":"note-val-1"}
BAR Processing piped input: {"title":"title-val-2","note":"note-val-2"}
BAR Processing piped input: {"title":"title-val-3","note":"note-val-3"}
2023-12-14T21-17-57PST
BAR Called from a pipeline
BAR Processing piped input: {"hi":"there"}
Children