Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: December 25, 2019
We’ve previously examined how to pass command-line arguments to a Bash script. Command-line arguments are a crucial feature in Bash scripting, enabling the script to take input values at runtime, thereby increasing its flexibility and usefulness.
By using these arguments, we pass data or options to a script when it runs, eliminating the need for hard-coded values inside the script. This approach allows the script to handle different scenarios, making it more dynamic and reusable.
In this tutorial, we’ll explore the various ways we can use command-line arguments in a Bash script. We’ll start by covering positional parameters and then move on to more advanced techniques like flags, loops, and the shift operator.
Bash provides multiple ways to process the arguments passed to a script. Thus, the choice of usage depends on the number of arguments, their order, and how we plan to use them. Let’s break down these approaches step by step.
When running a Bash script, the input arguments are stored in special variables:
Let’s further explain with an example:
$ ./example.sh arg1 arg2 arg3
Here, we can use the various types of special variables. The special parameter $@ represents the arguments arg1, arg2, and arg3, while $# is 3, standing for the number of arguments, and $0 is ./example.sh, the name of the Bash script.
These variables form the foundation for more advanced techniques in argument processing.
Let’s take a look at the different ways to process the arguments passed to a Bash script inside the script.
Arguments passed to a script are processed in the same order in which they’re sent. The indexing of the arguments starts at one, and the first argument can be accessed inside the script using $1.
Similarly, the second argument can be accessed using $2, and so on. The positional parameters refer to the representation of arguments based on their position in the command line.
As an example, let’s consider the userReg-positional-parameter.sh script, which prints positional parameter values corresponding to Username, Age, and Full Name in that order:
$ cat userReg-positional-parameter.sh
echo "Username: $1";
echo "Age: $2";
echo "Full Name: $3";
Now let’s run this script with the three input parameters:
$ sh userReg-positional-parameter.sh john 25 'John Smith'
Username : john
Age: 25
Full Name: John Smith
The result shows that the first positional parameter, $1, is john, while $2 is 25, and $3 is John Smith.
However, this approach has its limitations, especially when we don’t know the number of arguments or when their order may vary. For this reason, let’s explore argument validation next.
It’s important to validate the input to ensure that the correct number of arguments has been provided. In this case, we use $# to check the number of arguments and handle the error gracefully if the expected number of inputs isn’t met.
Let’s copy and edit the previous Bash script, userReg-positional-parameter.sh, to include an argument validation check before the main part of the script is executed. In other words, the validation part of the script prints a message and exits when the number of arguments passed is wrong based on what the script requires. We’ll name the new script userReg-validation-positional-parameter.sh:
$ cat userReg-validation-positional-parameter.sh
# Argument validation check
if [ "$#" -ne 3 ]; then
echo "Usage: $0 <username> <age> <fullname>"
exit 1
fi
# The main code
echo "Username: $1";
echo "Age: $2";
echo "Full Name: $3";
The first section of the script is for validating the number of arguments, while the second section represents the main code. However, the main code doesn’t run if the condition in the validation check isn’t met.
Let’s test this script out:
$ sh userReg-validation-positional-parameter.sh john 25
Usage: userReg-validation-positional-parameter <username> <age> <fullname>
When only two arguments are supplied, the code exits and prints the usage syntax for the Bash script.
This type of validation is a simple, yet essential, step to prevent runtime errors and ensure the script behaves as expected.
Using flags is a common way of passing input to a script. When passing input to the script, there’s a flag (usually a single letter) starting with a hyphen (–) before each argument.
Let’s take a look at the userReg-flags.sh script, which takes three arguments: username (-u), age (-a), and fullname (-f).
We’ll modify the userReg-positional-parameter.sh script to use flags instead of relying on positional parameters. The getopts function reads the flags in the input, and the OPTARG variable refers to their corresponding values:
$ cat userReg-flags.sh
while getopts u:a:f: flag
do
case "${flag}" in
u) username=${OPTARG};;
a) age=${OPTARG};;
f) fullname=${OPTARG};;
esac
done
echo "Username: $username";
echo "Age: $age";
echo "Full Name: $fullname";
Here, we’re using the getopts function to parse the flags provided as input and the case block to assign the value specified to the corresponding variable.
Let’s run this script with the same input as before, only this time we’ll add flags to the input:
$ sh userReg-flags.sh -f 'John Smith' -a 25 -u john
Username : john
Age: 25
Full Name: John Smith
The output is the same as before, though we have shifted the positions of the username and fullname arguments.
Therefore, we’ve learned that this method removes the reliance on the order of arguments, making the script more flexible.
In this section, let’s explore more methods for processing the input of a Bash script. Now, we can touch on methods that are a little bit more advanced.
Positional parameters combined with flags produce greater flexibility in terms of usage in a Bash script. For instance, we can have optional parameters handled by flags and mandatory ones handled as positional parameters.
Let’s illustrate this using the Bash script flag-positional-param.sh:
$ cat flag-positional-param.sh
while getopts u:a:f: flag
do
case "${flag}" in
u) username=${OPTARG};;
a) age=${OPTARG};;
f) fullname=${OPTARG};;
esac
done
# Now handle positional arguments
shift $((OPTIND - 1))
param1=$1 # First positional argument
param2=$2 # Second positional argument
param3=$3 # Third positional argument
echo "Username: $username";
echo "Age: $age";
echo "Full Name: $fullname";
echo "First Positional Parameter: $param1";
echo "Second Positional Parameter: $param2";
echo "Third Positional Parameter: $param3";
First, the script parses the -u, -a, and -f options using getopts. Moreover, these correspond to username, age, and fullname, respectively. After getopts processes the flags, OPTIND points to the next argument, which will become the first positional argument. So, by using shift $((OPTIND – 1)), we skip over the parsed options and their values.
Subsequently, $1, $2, and $3 represent the positional arguments passed after the flags. We assign these to the param1, param2, and param3 variables, respectively.
Let’s run this script with the same input as before, only this time, we’ll add flags and positional parameters to the input:
$ sh flag-positional-param.sh -u john -f "John Smith" -a 25 param1_value param2_value param3_value
Username: john
Age: 25
Full Name: John Smith
First Positional Parameter: param1_value
Second Positional Parameter: param2_value
Third Positional Parameter: param3_value
This approach allows working with both flags and positional arguments in Bash scripts efficiently.
Positional parameters, while convenient in many cases, can’t be used when the input size is unknown. The use of a loop construct comes in handy in these situations.
The special parameter $@ is an array-like construct of all the input arguments. Using $@ within a for loop, we can iterate over the input and process all the arguments passed.
Let’s consider the users-loop.sh script, which prints all the usernames that have been passed as input:
$ cat users-loop.sh
i=1;
for user in "$@"
do
echo "Username - $i: $user";
i=$((i + 1));
done
Now, let’s run the script:
$ sh users-loop.sh john matt bill 'joe wicks' carol
Username - 1: john
Username - 2: matt
Username - 3: bill
Username - 4: joe wicks
Username - 5: carol
In this example, we’re iterating the user variable over the entire array of input arguments. This iteration starts at the first input argument, john, and runs until the last argument, carol, despite the fact that the number of arguments can’t be known by the script beforehand.
The shift operator in Bash (syntactically shift n, where n is the number of positions to move) shifts the position of the command-line arguments. The default value for n is one if not specified.
The shift operator causes the indexing of the input to start from the shifted position. In other words, using this operator on an array-like input shifts the positional parameter $1 to the argument that is n positions to the right of the current value of $1.
Consider an example script that determines whether the input is odd or even:
$ sh parityCheck.sh 13 18 27 35 44 52 61 79 93
We now know that $1 refers to the first argument, which is 13. Using the shift operator with input 1 (shift 1) causes the indexing to start from the second argument. That is, $1 now refers to the second argument (18). Similarly, calling shift 2 will then cause the indexing to start from the fourth argument (35).
Let’s again take a look at the example of the users-loop.sh script. Instead of using the $@ variable and iterating over it, we’ll now use the shift operator in users-shift-operator.sh. The $# variable returns the input size:
$ cat users-shift-operator.sh
i=1;
j=$#;
while [ $i -le $j ]
do
echo "Username - $i: $1";
i=$((i + 1));
shift 1;
done
Let’s run the script with the same input as before:
$ sh users-shift-operator.sh john matt bill 'joe wicks' carol
Username - 1: john
Username - 2: matt
Username - 3: bill
Username - 4: joe wicks
Username - 5: carol
In this example, we’re shifting the positional parameter in each iteration by one until we reach the end of the input. Therefore, $1 refers to the next element in the input each time.
In this article, we explored various ways to process the arguments passed to a Bash script during runtime. In particular, we discussed positional parameters and flags, in addition to looping over the input arguments and using the shift operator.
By employing the techniques discussed in this article, we can create robust and dynamic Bash scripts capable of handling user input effectively.
As always, the examples used in this article are available over on GitHub.