Homepage › Forums › Articles › Programming › Bash Constructs
Tagged: bash, constructs, introduction, programming, script, shell
This topic was published by DevynCJohnson and viewed 1608 times since "". The last page revision was "".
- Topics - 437
In BASH scripting, one of the most important parts of the language (or any programming language) is the decision constructs. Decision constructs allow programs and scripts to make decisions. Loops are also like decision constructs; loops perform a task if and while the statement is true. The constructs that BASH supports are if, while, until, and for.
If-constructs perform a task if a statement is true. For example, a user could write code that prints the date if the input equals date.
read INPUT if [ "$INPUT" = "date" ] then date fi
The "read" command takes the user's typed input and saves it to the variable "INPUT". Next, if the input is "date", then the current date is printed. The command "fi" indicates the end of the construct. This code can be written and modified many ways. For example, what if the input is not "date"? Then, a programmer could add an "else" or "elif" statement.
read INPUT if [ "$INPUT" = "date" ] then date elif [ "$INPUT" = "" ] then echo "You turned in a blank input." else echo "I do not know what to do." fi
The code performs the same action, but if the input is blank, then the script informs the user. If all of the statements are false, then the commands associated with the else-statement are executed. However, BASH is case-sensitive, so what happens if the user types "Date" instead of "date"? Well, the script would execute the else commands because "Date" makes the other statements false. Thankfully, this can be fixed. This is how the second line should look:
if [ "$INPUT" = "date" -o "$INPUT" = "Date" -o "$INPUT" = "DATE" ]
Now, if the user types "date", "Date", or "DATE", the current date will be printed. The "-o" means "or". Programmers can have as many alternate values as they wish. Developers could also use "-a" which means "and". Using this would mean that both statements must be true if a command is to execute. A statement can be reversed using a "not" statement. In most programming language, including BASH, this is symbolized with "!". For example, changing the second line to the one below will cause the script to print the current date as long as the input was not "date" or "Date". If the input is "date" or "Date", then the else commands will be executed.
if [ "$INPUT" != "date" -a "$INPUT" != "Date" ]
The whole code can be made to take up less lines by using semicolons. In BASH programming, a semicolon means the next command will be treated as if it has its own line. For illustration,
read INPUT if [ "$INPUT" = "date" ]; then date elif [ "$INPUT" = "" ]; then echo "You turned in a blank input." else echo "I do not know what to do." fi
This is called trolling. Not many programmers do this and many advise against it. The reason being that trolls are difficult for some people to read. Performance is not affected either way. This programming style can be used if the programmer prefers this over spacing out the code.
Notice that no semicolon is put directly after "then" or "else". For example, this will not work: "then;" and "else;". These two commands do not need it. In fact, a syntax error would result from using a semicolon after the commands.
NOTE: It is fine to use semicolons after each command on an then and else line. For example, to print the date and list of files and folders in a directory, do this "then date; ls" not "then; date; ls".
elif [ "$INPUT" = "" ]; then echo "You turned in a blank input."; echo "Retype something"; read NEWINPUT
An other useful construct is the while-loop. In a while loop, commands are executed while a statement remains true. For example,
while [ 1 == 1 ]; do echo "Aloha"; done
While 1 equals 1, the word "Aloha" will be printed to the screen forever. Programmers must be careful with while loops because bad programming will result in issues. For instance, the while loop above will never end until the user stops the script because one will always be equal to itself. This can be fixed by using the break command. The code below will stop when the loop is looping forever.
while [ 1 == 1 ]; do echo "Aloha"; break; done
Now, the word "Aloha" will be printed once instead of forever.
The until-loop will loop until a statement becomes true. If the above code is changed to an until-loop, then no output will be printed.
until [ 1 == 1 ]; do echo "Aloha"; done
This is because one already equals one. Until-loops are used when a task must be performed until the conditions change. For instance, a programmer could use an until-loop to count to nine from zero. Counters should be used with until-loops. A counter is a variable that holds a number that increases in numeric value. For illustration,
COUNTER=0 until [ $COUNTER == 10 ]; do echo "$COUNTER" let COUNTER=COUNTER+1 done
Or all on one line:
COUNTER=0; until [ $COUNTER == 10 ]; do echo "$COUNTER"; let COUNTER=COUNTER+1; done
Notice in until-loops, that instead of "then" we see "do" and "done" instead of "fi". Until, while, and for loops do not except "then" and "fi".
The other construct type is the for-loop. A for-loop receives a list of items and then performs an action on the item one at a time. The for-loop below gets a list of files from the ls command. One item at a time is saved in the "i" variable and then processed. In this case, the information is printed to the screen.
for i in $( ls ); do echo $i; done
Programmers can nest constructs. Nesting is putting a decision construct inside of another. Developers can have a for-loop in an if-statement that is in a while-loop. These constructs can be mixed and nested as much as needed. It is best for performance if the statements are kept within the same set of brackets instead of nested if constructs. For example, the first set of code below is better to use than the code after it. (The code below is indented for visual purposes. The code is not require to do this, but it can if a programmer wants to use that style.)
if [ "$INPUT" = "date" or "$INPUT" = "Date" ]; then date; fi
if [ "$INPUT" = "date" ] then date if [ "$INPUT" = "Date" ] then date fi fi
There are other conditions and statements that can be used with if, until, and while constructs. These other options are listed below. Please note that a single space is required around the comparison functions. For example, the first line below is valid and the second is invalid.
[ $COUNTER == 10 ] [ $COUNTER==10 ]
- -eq - is equal to - if [ "$X" -eq "$Y" ]
- -ne - is not equal to - if [ "$X" -ne "$Y" ]
- -gt - is greater than - if [ "$X" -gt "$Y" ]
- -ge - is greater than or equal to - if [ "$X" -ge "$Y" ]
- -lt - is less than - if [ "$X" -lt "$Y" ]
- -le - is less than or equal to - if [ "$X" -le "$Y" ]
- < - is less than - (("$X" < "$Y"))
- <= - is less than or equal to - (("$X" <= "$Y"))
- > - is greater than - (("$X" > "$Y"))
- >= - is greater than or equal to - (("$X" >= "$Y"))
The four if-constructs below are all valid. Double quotes may or may not be used with double parentheses or brackets. Most programmers do not use the quotes with double parentheses, but they do for the brackets. Depending on what data is being compared, a syntax error may occur if the quotes are missing. Many examples are given below.
num=1 if (( 1 < 2 )); then date; fi if (( $num < 2 )); then date; fi if (( "$num" < 2 )); then date; fi if (( "$num" < "2" )); then date; fi
- = - is equal to - if [ "$X" = "$Y" ]
- == - is equal to - if [ "$X" == "$Y" ] #NOTE: "=" is the same as "==" when the statement is in single brackets.
- != - is not equal to - if [ "$X" != "$Y" ]
- > - is greater than in ASCII alphabetical order - if [[ "$X" > "$Y" ]]
- < - is less than in ASCII alphabetical order - if [[ "$X" < "$Y" ]]
- -z - zero length (null)
- -n - string is not null.
NOTE: double brackets enable extra features like wildcards.
- [[ $y == x* ]] - wildcard matching
- [[ $y == "x*" ]] - Literal x* because of the quotes
- -e - file exists
- -f - file is a regular file (not a directory or device file)
- -s - file is not zero size
- -d - file is a directory
- -c - file is a character device like a terminal (tty) device file. Character devices send and receive one character at a time while block devices stream data in chunks called blocks.
- -p - file is a pipe. A pipe connects the stdout (program's output) to the stdin (program's input) of another program.
- -b - file is a block device. Block devices, like hard-drives, transmit data as blocks.
- -L - file is a symbolic link. Symbolic links are shortcuts.
- -h - file is a symbolic link. Symbolic links are shortcuts.
- -S - file is a socket. Unix sockets are similar to Internet sockets but without the network protocols. Also, they are like pipes, but the two applications can communicate back and forth.
- -r - file has read permission (for the user running the test)
- -w - file has write permission (for the user running the test)
- -x - file has execute permission (for the user running the test)
- -g - set-group-id (sgid) flag set on file or directory. If the sgid bit is set on an executable, then users can execute the executable with the owner's privileges.
- -u - set-user-id (suid) flag set on file. The suid bit, when set, allows a user to use an executable that they do not own.
- -k - sticky bit set. When the sticky-bit is set, the executed program will stay on the swap space even after the program is closed. The swap space is a paging-file filesystem for Linux. Keeping the program on the swap space speeds up the subsequent uses of the application. This is useful for commonly used programs.
- -O - you are owner of file
- -G - group-id of file same as yours. When this is true, it means that your user group owns the file.
- -N - file modified since it was last read
- X -nt Y - file X is newer than Y
- X -ot Y - file X is older than Y
- X -ef Y - files X and Y are hard links to the same file
Not (!) can be used with these statements to test for the opposite condition. This allows programmers to make a command execute only if a file is missing (! -e). In the code below, if the file "Linux.odt" does not exist, then the current date will be printed. Notice that there is a space on each side of the exclamation point, "-e", and the filename.
if [[ ! -e ./Linux.odt ]]; then date; fi
Using these constructs and comparisons can allow a programmer to design an artificial intelligence program. Normally, one if-construct contains a simple true or false choice. However, nesting many constructs and comparisons allow programs to make choices based for "the gray areas". This will make programs appear smarter and make them more self-reliant. Think about the popular game "Twenty-Questions". This game seems to always know what the user is thinking. Interestingly, this little game relies solely on a large nest of decision constructs. For example, if the thing is a vegetable that is yellow ..... and if it is smaller than a duck .... and if ... and if this..... then you are thinking of corn. If a programmer knows the best arrangement of constructs for their program, then they can easily make a powerful program. Else, they may want to gain a full understanding of decision constructs.