A Little Bash

(Bourne-Again SHell)

If you’re looking for root account and operating system tests, and operator input error handling for your bash script, you’ve come to the right place. The example commands I’m using are from an installation script that I created for my company using if and nested if statements. If statements are a type of conditional that tests one value against another to determine which action, defined by you, to take next. You determine if the operator input is valid or Invalid. The components of an if statement are if, then, else, elif, and fi. If, then, else, and fi will be covered in this article because they’re applicable. You won’t see else statements because they’re further down the script, but I will still cover them.

I realize I can use case statements, but I built this from a draft that I slowly built upon with if statements and a rework doesn’t have enough operational return to justify. I don’t use bashisms, so the commands are POSIX compatible (can be run using any shell variant). You can replace the question text, variable names, and values to test against to fit your needs.

The following snippet of script commands checks the following:

  • If you’ve executed the script with sudo privileges.
  • The operating system you’re on (in my example, Rocky and CentOS are tested), and if the test fails you’re informed of the failure and on which operating system you’ve attempted the installation.
  • Operator input against acceptable values, and if the test fails, the operator is asked to enter their answer again.
if [ $(id -u) != 0 ]; then
    echo "$(tput setaf 3)This script must be run as root.$(tput setaf 9)" 
    exit 1
fi

osrelease=$(awk -F= '$1=="ID" { print $2 ;}' /etc/os-release)

if [ $osrelease != '"rocky"' ] && [ $osrelease != '"centos"' ]; then
    echo "$(tput setaf 3)Please install on CentOS 7 or Rocky 8. You are trying to install on $(tput bold)$osrelease.$(tput setaf 9)"

    sleep 2
    exit 1
fi

if [ $osrelease == '"rocky"' ]; then

    echo "$(tput setaf 3)Are you deploying in a virtual private cloud or DMZ (yes/no)?$(tput setaf 9) "

    read answer

    if [ $answer != yes ] && [ $answer != y ]  && [ $answer != no ] && [ $answer != n ]; then
        echo "$(tput setaf 3)Please answer with yes or no.$(tput        setaf 9)"

        sleep 2

        echo "$(tput setaf 3)Are you deploying in a virtual private     cloud or DMZ (yes/no)?$(tput setaf 9) "

        read answer
    fi
    
    if [ $answer == yes ] || [ $answer == y ]; then

There are several things to note before I dig into what each command and command block performs.

  1. I use tput to control the foreground (setaf) color of the characters in quotes. In this example I’m using yellow (3) – $(tput setaf 3).
  2. You have to set the text color back to system default after each color declaration else all text will be the defined color – $(tput setaf 9).
  3. You must use single quotes around output that contains double quotes.
  4. I used single brackets instead of double brackets (bashism) for shell compatibility.
  5. Close each if statement with fi (finish) because you’ll get errors otherwise.
  6. The fi statement must be at the same level of identation as the if statement it’s closing.
if [ $(id -u) != 0 ]; then
    echo "$(tput setaf 3)This script must be run as root.$(tput setaf 9)" 
    exit 1
fi

This if statement runs the command “id -u” and tests the output against the value 0 using the not equal (!=) operator. If the command output is not equal to 0, you’re informed to run the script with root privileges using sudo/sudo -s/bash (whatever your preference) then the script terminates. If the command output tests true (“= 0” aka run with root privileges), the shell goes to the next command block.

osrelease=$(awk -F= '$1=="ID" { print $2 ;}' /etc/os-release)

Here, a variable named “osrelease” is assigned a value from the output of a command. The command “$(awk -F= '$1=="ID" { print $2 ;}' /etc/os-release)” looks for position 1 ($1) with a value of “ID” then prints to the terminal the value of position 2 ( print $2) in the file /etc/os-release using the awk utility. The -F option informs awk that a file with a path will be provided. Any time the “osrelease” variable is called ($osrelease) the entire awk command is executed.

if [ $osrelease != '"rocky"' ] && [ $osrelease != '"centos"' ]; then
    echo "$(tput setaf 3)Please install on CentOS 7 or Rocky 8. You are trying to install on $(tput bold)$osrelease.$(tput setaf 9)"

    sleep 2
    exit 1
fi

This if statement takes the value of the variable “osrelease” and tests it against the string “rocky” and “centos.” The output of the awk command above prints double quotes because they are present in position 2 of the line with “ID” (position 1) of the /etc/os-release file and must be encased in single quotes or you’ll get a syntax error. If position 2 doesn’t equal “rocky or “centos,” a message is printed to the terminal, informing the operator they’re trying to install on the incorrect operating system and on which operating system they attempting installation in bold lettering – $(tput bold). The double ampersand requires that both position 2 value tests fail before the failure message is printed to the terminal. The script terminates if both operating system tests fail. If one of the position 2 value tests passes, the shell continues to the next command block.

if [ $osrelease == '"rocky"' ]; then

    echo "$(tput setaf 3)Are you deploying in a virtual private cloud or DMZ (yes/no)?$(tput setaf 9) "
    read answer

This command block contains an if statement and an echo command. I have an else statement way down the script that contains CentOS 7 specific commands if the rocky test fails. The echo command prints to the terminal asking if the operator is deploying in a virtual private cloud (VPC) or a demilitarized zone (DMZ). The reason for this question is due to the firewall rule configuration difference between deploying on the internet and VPC or DMZ. The answer to the question is stored as the variable “answer,” and is called in the next if statement for testing.

I ask if the active firewall zone is the Trusted zone further down my script because some cloud providers set the active firewall zone to Public and others Trusted. No firewall rules are needed for a Trusted zone because all packets for all ports and protocols are accepted.

if [ $answer != yes ] && [ $answer != y ]  && [ $answer != no ] && [ $answer != n ]; then
        echo "$(tput setaf 3)Please answer with yes or no.$(tput        setaf 9)"

        sleep 2

        echo "$(tput setaf 3)Are you deploying in a virtual private     cloud or DMZ (yes/no)?$(tput setaf 9) "

        read answer
    fi

The value stored in $answer is tested against four acceptable answer formats that are yes, y, no, and n. All four of the tests must fail before a failure message is printed to the terminal to inform the operator that they must correct their answer. The script doesn’t issue the echo command for 2 seconds using the “sleep 2” command then the operator is asked the same question again. If one of the tests passes the shell goes to the next if statement. You can reassign shell script variable values. If the operator misspelled yes, no, n, or y, but correctly spells the answer the second time the original misspelled value is overwritten with the correct spelling.

if [ $answer == yes ] || [ $answer == y ]; then

Once we have a correctly spelled answer we test it against yes and y. If the answer is yes or y then you get a long list of commands to install various packages and our software for Rocky 8. If the value tests false then (using an else statement further down the script) you get commands appropriate for CentOS 7. There are a couple more questions further down the script but they contain the same logic covered in the provided snippet.

Happy scripting!