Process argument inputs to a Shell script using the "shift" command

When tasked to automate anything within Linux, you might find that Shell is the easiest tool to get the job done fast. Most of the time these shell scripts are built for a specific purpose and don’t have any need for configuration in different usage scenarios. However, it is possible to build a fully fleshed Command Line Interface (CLI) using a shell script, which I will outline for you the steps on how to do this.

Outline for the script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/bin/bash

# FUNCTIONS

show_help_and_exit() {
echo 'my_script.sh'
echo
echo 'Usage: ./my_script.sh [OPTS] COMMANDS [ARGS]'
echo
echo ' This is a template for showing how to structure a'
echo ' shell script to behave as a CLI.'
echo
echo 'Options:'
echo ' -d, --debug If this option is present, all debug output is displayed'
echo ' -o, --opt1 If this option is present, opt1 will be displayed to the'
echo ' screen.'
echo
echo 'Commands:'
echo ' cmd1 ARGS If this option is present, cmd1 and all arguments will be'
echo ' displayed to the screen.'
exit
}

echo_args() {
while [ -n "$1" ]; do
echo " $1"
shift
done
}

# PROCESS INPUT ARGUMENTS

if [ -z "$1" ]; then
show_help_and_exit
fi

while [ -n "$1" ] && [ -z "$DONE_PROCESSING_INPUT" ]; do
case $1 in
-h|--help)
show_help_and_exit
;;
-d|--debug)
DEBUG=true
if [ -n "$DEBUG" ]; then echo 'DEBUG: debug option set'; fi
shift
;;
-o|--opt1)
if [ -n "$DEBUG" ]; then echo 'DEBUG: opt1 option set'; fi
OPT1_OPTION=true
shift
;;
cmd1)
if [ -n "$DEBUG" ]; then echo 'DEBUG: cmd1 command set'; fi
CMD1_COMMAND=true
shift
CMD1_ARGS=( "$@" )
DONE_PROCESSING_INPUT=true
if [ -n "$DEBUG" ]; then echo 'DEBUG: done processing input'; fi
;;
*)
echo "ERROR: invalid argument $1. See --help for more info." 1>&2
exit 1
esac
done

# EXECUTE COMMANDS

# process opt1
if [ -n "$OPT1_OPTION" ]; then
if [ -n "$DEBUG" ]; then echo 'DEBUG: processing opt1'; fi
echo "opt1 was set"
fi

# process cmd1
if [ -n "$CMD1_COMMAND" ]; then
if [ -n "$DEBUG" ]; then echo 'DEBUG: processing cmd1'; fi
echo "cmd1 was set"
echo "args are:"
echo_args ${CMD1_ARGS[@]}
fi

Let’s go through this one section at a time.

Defining functions

First is defining functions for the script. Functions help to separate logic into reusable chunks, and they also help a lot to make the code more readable.

The first function I defined is show_help_and_exit(). This function simply displays the help menu using echo and then exits the script using exit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
show_help_and_exit() {
echo 'my_script.sh'
echo
echo 'Usage: ./my_script.sh [OPTS] COMMANDS [ARGS]'
echo
echo ' This is a template for showing how to structure a'
echo ' shell script to behave as a CLI.'
echo
echo 'Options:'
echo ' -d, --debug If this option is present, all debug output is displayed'
echo ' -o, --opt1 If this option is present, opt1 will be displayed to the'
echo ' screen.'
echo
echo 'Commands:'
echo ' cmd1 ARGS If this option is present, cmd1 and all arguments will be'
echo ' displayed to the screen.'
exit
}

The second function I defined is echo_args(). This function takes in a list of args as input and displays them in a list as output.

1
2
3
4
5
6
echo_args() {
while [ -n "$1" ]; do
echo " $1"
shift
done
}

To call a function in Shell, you just type the function name with a list of args, just as if you are using a terminal command. For example:

1
echo_args this is a list of args

This would simply output to the terminal

1
2
3
4
5
6
this
is
a
list
of
args

Processing input arguments

Let’s break this into chunks that can be easily explained.

First, when there is no input we want to display the help menu. For this we can use the show_help_and_exit() function we created before.

1
2
3
if [ -z "$1" ]; then
show_help_and_exit
fi

This if statement is checking if there is no arguments using -z and testing against the first argument using "$1". If there are no arguments, then it calls the show_help_and_exit() function. This function will then display the help menu and then exit the script.

Second, we need to loop through the arguments and capture information about which arguments are available.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
while [ -n "$1" ] && [ -z "$DONE_PROCESSING_INPUT" ]; do
case $1 in
-h|--help)
show_help_and_exit
;;
-d|--debug)
DEBUG=true
if [ -n "$DEBUG" ]; then echo 'DEBUG: debug option set'; fi
shift
;;
-o|--opt1)
if [ -n "$DEBUG" ]; then echo 'DEBUG: opt1 option set'; fi
OPT1_OPTION=true
shift
;;
cmd1)
if [ -n "$DEBUG" ]; then echo 'DEBUG: cmd1 command set'; fi
CMD1_COMMAND=true
shift
CMD1_ARGS=( "$@" )
DONE_PROCESSING_INPUT=true
if [ -n "$DEBUG" ]; then echo 'DEBUG: done processing input'; fi
;;
*)
echo "ERROR: invalid argument $1. See --help for more info." 1>&2
exit 1
esac
done

In this while loop we are continuing to loop until either the first argument is not valid, or until the variable DONE_PROCESSING_INPUT is defined. We then enter a case statement that takes in the first argument and then checks its value. The syntax -h|--help) will check if the first argument is either -h or --help and then execute the code within that case until the line with ;;.

Another For each argument and option we are setting an environment variable so later in the code we can easily check if that option or argument is set. We are also echoing debug statements if the DEBUG option is set, and setting this debug option using the -d or --debug option.

The last case *) is a catch-all and this is where we process invalid arguments. I simply echo to stderr helpful information to help the user debug this error.

This is the output when running this script with an invalid argument.

1
2
3
> ./my_script.sh not_a_valid_argument
ERROR: invalid argument not_a_valid_argument. See --help for more info.
(ERROR)-(Exit Code 1)-(General error)

Executing commands

After processing input we can then execute the commands based on the environment variables we set earlier.

1
2
3
4
5
6
7
8
9
10
11
12
13
# process opt1
if [ -n "$OPT1_OPTION" ]; then
if [ -n "$DEBUG" ]; then echo 'DEBUG: processing opt1'; fi
echo "opt1 was set"
fi

# process cmd1
if [ -n "$CMD1_COMMAND" ]; then
if [ -n "$DEBUG" ]; then echo 'DEBUG: processing cmd1'; fi
echo "cmd1 was set"
echo "args are:"
echo_args ${CMD1_ARGS[@]}
fi

As you can see here we are displaying debug output if -d or --debug is set, and also echoing that the options and commands are set.

We are also calling the echo_args function and passing in the contents of the array CMD1_ARGS that we set earlier in the code when processing cmd1 input and where we set its value to the rest of the arguments like so CMD1_ARGS=( "$@" ). The syntax echo_args ${CMD1_ARGS[@]} allows us to pass in the entire array as input to the echo_args function.

Running the script

Now is time to run the script!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> ./template_shell_script.sh -d -o cmd1 this is a list of arguments
DEBUG: debug option set
DEBUG: opt1 option set
DEBUG: cmd1 command set
DEBUG: done processing input
DEBUG: processing opt1
opt1 was set
DEBUG: processing cmd1
cmd1 was set
args are:
this
is
a
list
of
arguments

This template is a good base to go off of when developing your own simple CLI within a Shell script. Good luck on all your projects!

Get new posts by email:

Comments

Hint: To type code with Disqus, use
<code><pre>insert.code.here()<pre/><code/>