Bash Scripts — Part 3 — Command-Line Options and Switches
Having mastered the previous parts of this series, you learned about what bash scripts are, how to write them, how to control the flow of a program, how to work with files. Today we will talk about how to add interactivity to scripts, equipping them with capabilities to receive data from the user and to process this data.
The most common way to pass data to scripts is by using command line parameters. By calling the script with parameters, we give it some information with which it can work. It looks like this:
$ ./myscript 10 20In this example, the script is passed with two arguments — “10” and “20”. All this is good, but how do you read the data in the script?
Reading command line parameters
The bash shell assigns command line parameters entered when invoking the script to special variables called positional parameters:
$0- the name of the script.$1- first parameter.$2- the second parameter - and so on, up to the variable$9that contains the ninth parameter.
Here’s how you can use command line parameters in a script using these variables:
#!/bin/bash
echo $0
echo $1
echo $2
echo $3Let’s run the script with parameters:
./myscript 5 10 15
This is what it will output to the console:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 5 10 15
./myscript
5
10
15Please note that command line parameters are separated by spaces.
Let’s take a look at another example of using parameters. Here we will find the sum of the numbers passed to the script:
#!/bin/bash
total=$[ $1 + $2 ]
echo "The first parameter is $1."
echo "The second parameter is $2."
echo "The sum is $total."Let’s run the script and check the calculation result.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 5 10
The first parameter is 5.
The second parameter is 10.
The sum is 15.Command-line parameters do not have to be numbers. You can also pass strings to scripts. For example, here’s a script that works with a string:
#!/bin/bash
echo "Hello $1, how do you do"Let’s run it:
./myscript Adam
He will output what we expect from him:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript Adam
Hello Adam, how do you doWhat if the parameter contains spaces, and we need to process it as an independent piece of data? We assume that if you have mastered the previous parts of this guide, you already know the answer. It consists in the use of quotes.
If the script needs more than nine parameters when accessing them, the number in the variable name must be enclosed in curly braces, for example:
${10}Parameter check
If the script is called without parameters, but the code is supposed to have them for normal operation, an error will occur. Therefore, it is recommended to always check for the presence of parameters passed to the script when invoked. For example, it can be organized like this:
#!/bin/bash
if [ -n "$1" ]
then
echo "Hello $1."
else
echo "No parameters found."
fiLet’s call the script first with a parameter, and then without parameters.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript Adam
Hello Adam.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
No paremeters found.Counting parameters
In the script, you can count the number of parameters passed to it. The bash shell provides a special variable for this. Namely, the variable $#contains the number of parameters passed to the script when called.
Let’s try it out:
#!/bin/bash
echo "There were $# parameters passed."Let’s call the script.
./myscript 1 2 3 4 5As a result, the script will report that five parameters have been passed to it.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 1 2 3 4 5
There were 5 parameters passed.This variable provides an unusual way to get the last of the parameters passed to the script, which does not require knowing their number. This is how it looks:
#!/bin/bash
echo "The last parameter was ${!#}"Let’s call the script and see what it outputs:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 1 2 3 4 5
The last parameter was 5Capturing all command-line options
In some cases, you need to capture all the parameters passed to the script. To do this, you can use the variables $*and $@. They both contain all the command line parameters, making it possible to access what is passed to the script without using positional parameters.
The variable $*contains all the parameters entered on the command line in the form of a single "word."
In the variable, the $@parameters are split into separate "words." These parameters can be iterated over in loops.
Let’s see the difference between these variables with examples. Let’s take a look at their contents first:
#!/bin/bash
echo "Using the \$* method: $*"
echo "-----------"
echo "Using the \$@ method: $@"Here is the script output:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 1 2 3 4 5
Using the \$* method: 1 2 3 4 5
-----------
Using the \$@ method: 1 2 3 4 5As you can see, the output of both variables is the same. Now let’s try to go through the contents of these variables in loops in order to see the difference between them:
#!/bin/bash
count=1
for param in "$*"
do
echo "\$* Parameter #$count = $param"
count=$(( $count + 1 ))
done
count=1
for param in "$@"
do
echo "\$@ Parameter #$count = $param"
count=$(( $count + 1 ))
doneTake a look at what the script outputs to the console. The difference between the variables is pretty apparent.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 1 2 3 4 5
$* Parameter #1 = 1 2 3 4 5
$@ Parameter #1 = 1
$@ Parameter #2 = 2
$@ Parameter #3 = 3
$@ Parameter #4 = 4
$@ Parameter #5 = 5A variable$*contains all parameters passed to the script as a single piece of data, while in a variable$@they are represented by independent values. Which variable to use depends on what exactly is needed in a particular scenario.
Shift command
Use the command shiftin bash scripts with caution, as it literally shifts the values of positional parameters.
When you use this command, it shifts the positional parameter values to the left by default. For example, the value of a variable $3becomes the value of a variable $2, the value $2goes to $1, and what was before that is $1, lost. Note that this $0does not change the value of the variable containing the script name.
Using the command shift, let's consider another way to iterate over the parameters passed to the script:
#!/bin/bash
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$(( $count + 1 ))
shift
doneThe script runs a loop while, checking the length of the first parameter value. When the length becomes zero, the loop is exited. After checking the first parameter and displaying it on the screen, a command is called shiftthat shifts the parameter values by one position.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 1 2 3 4 5
Parameter #1 = 1
Parameter #2 = 2
Parameter #3 = 3
Parameter #4 = 4
Parameter #5 = 5When using the commandshift, remember that each time it is called, the value of the variable is$1irretrievably lost.
Command-line switches
Command-line switches usually look like letters preceded by a dash. They serve to manage scripts. Consider this example:
#!/bin/bash
echo
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option" ;;
-b) echo "Found the -b option" ;;
-c) echo "Found the -c option" ;;
*) echo "$1 is not an option" ;;
esac
shift
doneLet’s run the script:
$ ./myscript –a –b –c –dAnd let’s analyze what it displays in the terminal.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript -a -b -c -d
Found the -a option
Found the -b option
Found the -c option
-d is not an optionThis code uses a constructioncasethat checks the key passed to it against the list of keys handled by the script. If the passed value is found in this list, the corresponding branch is executed. If, when calling the script, any key is used, the processing of which is not provided, the "*" branch will be executed.
How to distinguish between keys and parameters
Often, when writing bash scripts, a situation arises when you need to use both command line parameters and switches. The standard way to do this is to use a special character sequence that tells the script when keys end and normal parameters begin.
This sequence is a double dash (-). The shell uses it to indicate the position at which the list of keys ends. After the script detects the sign of the end of the keys, what is left can, without fear of errors, be processed as parameters, and not as keys. Let’s consider an example:
#!/bin/bash
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option" ;;
-b) echo "Found the -b option";;
-c) echo "Found the -c option" ;;
--) shift
break ;;
*) echo "$1 is not an option";;
esac
shift
done
count=1
for param in $@
do
echo "Parameter #$count: $param"
count=$(( $count + 1 ))
doneThis script uses the command breakto interrupt the loop whilewhen it encounters a double dash on a line.
Here’s what happens after calling it.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript -a -b -c -- 5 10 15
Found the -a option
Found the -b option
Found the -c option
Parameter #1: 5
Parameter #2: 10
Parameter #3: 15As you can see, when the script, parsing the data passed to it, finds a double dash, it finishes processing keys and considers everything that has not yet been processed as parameters.
Handling keys with values
As your scripts become more complex, you will face situations where ordinary keys are no longer enough, which means that you will need to use keys with certain values. For example, a script call that uses this feature looks like this:
./myscript -a test1 -b -c test2The script should be able to determine when additional parameters are used together with command-line switches:
#!/bin/bash
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option";;
-b) param="$2"
echo "Found the -b option, with parameter value $param"
shift ;;
-c) echo "Found the -c option";;
--) shift
break ;;
*) echo "$1 is not an option";;
esac
shift
done
count=1
for param in "$@"
do
echo "Parameter #$count: $param"
count=$(( $count + 1 ))
doneLet’s call this script like this:
./myscript -a -b test1 -dLet’s look at the results of his work.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript -a -b 15 -d
Found the -a option
Found the -b option, with parameter value 15
Found the -d optionIn this example, three keys are processed in the construct. The key-brequires an additional parameter. Since the key being processed is in a variable$1, the corresponding parameter will be in$2(the command is used hereshift, therefore as processing proceeds, everything that is passed to the script is shifted to the left). When we figured it out, it remains only to extract the value of the variable$2, and we will have the parameter of the required key. Of course, one more command is needed hereshiftin order for the next key to getting into$1.
Using standard keys
When writing bash scripts, you can choose any letters for command line keys and arbitrarily set the script’s reaction to these keys. However, in the Linux world, the values of some keys have become something of a standard that is useful to adhere to. Here is a list of these keys:
-aList all objects.-cMake a count.-dSpecify directory.-eExpand the object.-fSpecify the file from which to read data.-hDisplay command help.-iIgnore case.-lPerform full format data output.-nUse non-interactive (batch) mode.-oAllows you to specify the file to which you want to redirect the output.-qExecute the script in quiet mode.-rProcess folders and files recursively.-sExecute the script in silent mode.-vExecute verbose output.-xExclude object.-yAnswer "yes" to all questions.
If you are on Linux, you are likely familiar with many of these keys. By using them in their common sense in your scripts, you can help users interact with them without having to worry about reading the documentation.
Receiving data from the user
Command-line switches and parameters are a great way to get data from the user of the script, but in some cases, more interactivity is needed.
Sometimes scripts need data that the user must enter during program execution. The bash shell has a command for this very purpose read.
This command allows you to accept input either from standard input (keyboard) or using other file descriptors. After receiving the data, this command puts it into a variable:
#!/bin/bash
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program."Note that the command echothat displays the prompt is invoked with a switch -n. This results in no line feed being displayed at the end of the prompt, which allows the user of the script to enter data where the prompt appears rather than on the next line.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Enter your name: Adam
Hello Adam, welcome to my program.When calling,readyou can specify several variables:
#!/bin/bash
read -p "Enter your name: " first last
echo "Your data for $last, $first..."This is what the script will output after running.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Enter you name: first last
Your data for last, first...If, by callingread, do not specify a variable, the data entered by the user will be placed in a special environment variableREPLY:
#!/bin/bash
read -p "Enter your name: "
echo Hello $REPLY, welcome to my program.This will produce the following behavior:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Enter your name: Adam
Hello Adam, welcome to my program.If the script should continue execution regardless of whether the user enters some data or not,readyou can use the key when invoking the command-t. Namely, the key parameter sets the waiting time for input in seconds:
#!/bin/bash
if read -t 5 -p "Enter your name: " name
then
echo "Hello $name, welcome to my script"
else
echo "Sorry, too slow!"
fiIf no data is entered within 5 seconds, the script will execute the conditional statement branch else, outputting an apology.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Enter your name: Sorry, to slow!Entering passwords
Sometimes it is better not to show what the user enters in response to the script question. For example, this is usually done when asking for passwords. The -s command key readprevents keyboard input from being displayed on the screen. In fact, the data is output, but the command readmakes the text color the same as the background color.
#!/bin/bash
read -s -p "Enter your password: " pass
echo "Is your password really $pass? "This is how this script will work.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Enter your password: Is you password really secretpass?Reading data from a file
The command readcan, on each call, read one line of text from the file. When there are no more unread lines in the file, it will simply stop. If you need to get the entire contents of a file in a script, you can, using a pipeline, transfer the results of calling a command catfor a file, a structure whilethat contains a command read (of course, using the command catlooks primitive, but our goal is to show everything as simply as possible, focusing on beginners; experienced users, we are sure they will understand it).
Let’s write a script that uses the approach to reading files just described.
#!/bin/bash
count=1
cat myfile | while read line
do
echo "Line $count: $line"
count=$(( $count + 1 ))
done
echo "Finished"Let’s look at it in action.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line 1: hello
Line 2: this
Line 3: is
Line 4: test
FinishedHere we passed thewhilecontents of the file into a loop and went through all the lines of this file, displaying the number and contents of each of them.
Outcome
Today we have examined working with switches and command line parameters. Without these tools, the scope for scripting is extremely narrow. Even if the script is written, as they say, “for me.” Here we examined the approaches to receiving data from the user during program execution — this makes the scripts interactive.
Next time, let’s talk about input and output operations.
Dear Readers! Thank you for sharing your experience in the comments on the previous parts of this series. If you have something to say about the processing of everything that can be passed to the script at startup or while it is running, we are sure that many will be interested in reading about it.
