Baeldung Pro – Linux – NPI EA (cat = Baeldung on Linux)
announcement - icon

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.

Partner – Orkes – NPI EA (tag=Kubernetes)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

1. Introduction

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.

2. Processing the Input

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.

2.1. Basic Command-Line Argument Handling

When running a Bash script, the input arguments are stored in special variables:

  • $@: this contains all the input arguments
  • $#: the number of arguments passed to the script
  • $0: the name of the script itself

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.

2.2. Positional Parameters

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.

2.3. Argument Validation

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.

2.4. Flags

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.

3. More Input Processing Methods

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.

3.1. Combining Flags and Positional Parameters

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.

3.2. Loop Construct

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.

3.3. Shift Operator

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.

4. Conclusion

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.

5 Comments
Oldest
Newest
Inline Feedbacks
View all comments