Advanced Bash Scripting

Special Shell Variables

Args

When writing Bash scripts, handling command-line arguments efficiently is crucial. You can access each argument by its position ($1, $2, …), but when the number of parameters varies, special variables like $@ and $* simplify your logic. This guide covers:

  • Positional parameters
  • Grouping arguments
  • Iterating with "$@" vs "$*"
  • The impact of quoting
  • Customizing the internal field separator (IFS)
  • Compatibility considerations

1. Positional Parameters: $1, $2, …

By default, Bash assigns each argument to a numbered variable:

#!/usr/bin/env bash
firstarg=$1
secondarg=$2

echo "First argument: ${firstarg}"
echo "Second argument: ${secondarg}"
$ ./simple-args.sh "arg1"
First argument: arg1
Second argument:

$ ./simple-args.sh "arg1" "arg2"
First argument: arg1
Second argument: arg2

When you need to accept an unpredictable number of parameters, indexing each one becomes cumbersome. That’s where $@ and $* come in.


2. Grouping All Arguments: $@ vs $*

Both $@ and $* collect all positional arguments:

The image illustrates the special shell variables `$*` and `$@`, accompanied by a graphic of twelve cylindrical objects arranged in a grid.

#!/usr/bin/env bash
packaged_args1="$@"
packaged_args2="$*"

echo "Using \$@: ${packaged_args1}"
echo "Using \$*: ${packaged_args2}"
$ ./simple-args2.sh arg1 arg2 arg3
Using $@: arg1 arg2 arg3
Using $*: arg1 arg2 arg3

Both variables contain the full list of parameters, but they differ when you iterate over them.


3. Iterating with a For Loop

Compare two scripts that loop over their arguments:

#!/usr/bin/env bash
echo "Number of arguments: $#"
echo "All arguments: $@"
for arg in "$@"; do
  echo "Argument: $arg"
done
#!/usr/bin/env bash
echo "Number of arguments: $#"
echo "All arguments: $*"
for arg in "$*"; do
  echo "Argument: $arg"
done
$ ./atsign-example1.sh one two three
Number of arguments: 3
All arguments: one two three
Argument: one
Argument: two
Argument: three

$ ./star-example1.sh one two three
Number of arguments: 3
All arguments: one two three
Argument: one two three

The image illustrates the concept of special shell variables `$*` and `$@`, using a visual representation of containers and arrows.

In the soda‐can analogy:

  • $@ places each can in its own compartment.
  • $* pours all the soda into one big bottle—individual cans are no longer separate.

The image illustrates the difference between special shell variables `$@` and `$*`, using a visual representation of containers to show how they handle arguments.


4. The Importance of Double Quotes

Note

Always quote $@ and $*:

  • "$@" expands each argument separately.
  • "$*" joins all arguments into a single string, separated by the first character of IFS (default: space).

Unquoted, both behave identically:

#!/usr/bin/env bash
print_section_header() {
  local title="$1"
  echo "==============================="
  echo "= Section ${title} ="
  echo "==============================="
}

print_section_header "1: \$@"
echo "--> Output of \$@: $@"
echo "Looping \$@ without quotes:"
for arg in $@; do
  echo "$arg"
done

print_section_header "2: \$*"
echo "--> Output of \$*: $*"
echo "Looping \$* without quotes:"
for arg in $*; do
  echo "$arg"
done
$ ./special-shell-noquotes.sh one two three
===============================
= Section 1: $@ =
===============================
--> Output of $@: one two three
Looping $@ without quotes:
one
two
three
===============================
= Section 2: $* =
===============================
--> Output of $*: one two three
Looping $* without quotes:
one
two
three

5. Modifying the Internal Field Separator (IFS)

You can change IFS to alter how "$*" joins arguments:

#!/usr/bin/env bash
IFS=','

echo "Output of \$@: $@"
echo "Output of \$*: $*"
$ ./modified-ifs-v1.sh one two three
Output of $@: one two three
Output of $*: one,two,three

Splitting later requires unquoted iteration:

#!/usr/bin/env bash
IFS='_'
args_at="$@"
args_star="$*"

print_section_header() { ... }

print_section_header "1: \$@"
echo "--> \$@: $@"
echo "Looping over args_at:"
for arg in $args_at; do
  echo "$arg"
done

print_section_header "2: \$*"
echo "--> \$*: $*"
echo "Looping over args_star:"
for arg in $args_star; do
  echo "$arg"
done

6. Compatibility with Older Bash Versions

Some pre-4.0 Bash releases split unquoted assignments differently. For example, under Bash 3:

#!/usr/bin/env bash
IFS=','
args_at=$@
echo "--> \$@: ${args_at}"
for arg in $args_at; do
  echo "$arg"
done

Still, quoting on assignment ensures consistent splitting:

#!/usr/bin/env bash
IFS=','
args_at="$@"
echo "--> \$@: $@"
for arg in $args_at; do
  echo "$arg"
done

7. Summary: $@ vs $*

VariableQuoted ExpansionUnquoted ExpansionUse Case
$@Each arg is separateEach word separateLooping over arguments
$*All args as oneSplits on IFSPassing all args to another command or function

8. Conclusion

  • Use "$@" when you need to preserve each argument.
  • Use "$*" to aggregate them into a single string with a custom delimiter.
  • Always quote both to maintain consistent behavior across Bash versions and avoid word-splitting pitfalls.

The image shows a comparison between special shell variables `$@` and `$*`, with a recommendation to surround them with double quotes.


Watch Video

Watch video content

Previous
ifs
Next
pid