Science and technology

How to program with Bash: Logical operators and shell expansions

Bash is a strong programming language, one completely designed to be used on the command line and in shell scripts. This three-part sequence (which is predicated on my three-volume Linux self-study course) explores utilizing Bash as a programming language on the command-line interface (CLI).

The first article explored some easy command-line programming with Bash, together with utilizing variables and management operators. This second article seems to be into the forms of file, string, numeric, and miscellaneous logical operators that present execution-flow management logic and various kinds of shell expansions in Bash. The third and remaining article within the sequence will discover the for, whereas, and till loops that allow repetitive operations.

Logical operators are the premise for making choices in a program and executing totally different units of directions based mostly on these choices. This is typically known as stream management.

Logical operators

Bash has a big set of logical operators that can be utilized in conditional expressions. The most simple type of the if management construction checks for a situation after which executes an inventory of program statements if the situation is true. There are three forms of operators: file, numeric, and non-numeric operators. Each operator returns true (zero) if the situation is met and false (1) if the situation shouldn’t be met.

The purposeful syntax of those comparability operators is one or two arguments with an operator which are positioned inside sq. braces, adopted by an inventory of program statements which are executed if the situation is true, and an non-obligatory record of program statements if the situation is fake:

if [ arg1 operator arg2 ] ; then record
or
if [ arg1 operator arg2 ] ; then record ; else record ; fi

The areas within the comparability are required as proven. The single sq. braces, [ and ], are the standard Bash symbols which are equal to the take a look at command:

if take a look at arg1 operator arg2 ; then record

There can also be a newer syntax that provides a number of benefits and that some sysadmins want. This format is a bit much less suitable with totally different variations of Bash and different shells, comparable to ksh (the Korn shell). It seems to be like:

if [[ arg1 operator arg2 ]] ; then record

File operators

File operators are a strong set of logical operators inside Bash. Figure 1 lists greater than 20 totally different operators that Bash can carry out on information. I take advantage of them fairly steadily in my scripts.

Operator Description
-a filename True if the file exists; it may be empty or have some content material however, as long as it exists, this might be true
-b filename True if the file exists and is a block particular file comparable to a tough drive like /dev/sda or /dev/sda1
-c filename True if the file exists and is a personality particular file comparable to a TTY system like /dev/TTY1
-d filename True if the file exists and is a listing
-e filename True if the file exists; this is similar as -a above
-f filename True if the file exists and is an everyday file, versus a listing, a tool particular file, or a hyperlink, amongst others
-g filename True if the file exists and is set-group-id, SETGID
-h filename True if the file exists and is a symbolic hyperlink
-k filename True if the file exists and its “sticky'” bit is about
-p filename True if the file exists and is a named pipe (FIFO)
-r filename True if the file exists and is readable, i.e., has its learn bit set
-s filename True if the file exists and has a dimension larger than zero; a file that exists however that has a dimension of zero will return false
-t fd True if the file descriptor fd is open and refers to a terminal
-u filename True if the file exists and its set-user-id bit is about
-w filename True if the file exists and is writable
-x filename True if the file exists and is executable
-G filename True if the file exists and is owned by the efficient group ID
-L filename True if the file exists and is a symbolic hyperlink
-N filename True if the file exists and has been modified because it was final learn
-O filename True if the file exists and is owned by the efficient person ID
-S filename True if the file exists and is a socket
file1 -ef file2 True if file1 and file2 discuss with the identical system and iNode numbers
file1 -nt file2 True if file1 is newer (based on modification date) than file2, or if file1 exists and file2 doesn’t
file1 -ot file2 True if file1 is older than file2, or if file2 exists and file1 doesn’t

Fig. 1: The Bash file operators

As an instance, begin by testing for the existence of a file:

[scholar@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File doesn't exist." ; fi
The file Take a look atFile1 doesn't exist.
[scholar@studentvm1 testdir]$

Next, create a file for testing named Take a look atFile1. For now, it doesn’t have to include any information:

[scholar@studentvm1 testdir]$ contact Take a look atFile1

It is straightforward to vary the worth of the $File variable fairly than a textual content string for the file title in a number of places on this brief CLI program:

[scholar@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] ; then echo "The file $File exists." ; else echo "The file $File doesn't exist." ; fi
The file Take a look atFile1 exists.
[scholar@studentvm1 testdir]$

Now, run a take a look at to find out whether or not a file exists and has a non-zero size, which implies it comprises information. You need to take a look at for 3 circumstances: 1. the file doesn’t exist; 2. the file exists and is empty; and three. the file exists and comprises information. Therefore, you want a extra advanced set of checks—use the elif stanza within the if-elif-else assemble to check for all the circumstances:

[scholar@studentvm1 testdir]$ File="TestFile1" ; if [ -s $File ] ; then echo "$File exists and comprises information." ; fi
[scholar@studentvm1 testdir]$

In this case, the file exists however doesn’t include any information. Add some information and take a look at once more:

[scholar@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; fi
Take a look atFile1 exists and comprises information.
[scholar@studentvm1 testdir]$

That works, however it is just really correct for one particular situation out of the three doable ones. Add an else stanza so that you may be considerably extra correct, and delete the file so you may absolutely take a look at this new code:

[scholar@studentvm1 testdir]$ File="TestFile1" ; rm $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; else echo "$File doesn't exist or is empty." ; fi
Take a look atFile1 doesn't exist or is empty.

Now create an empty file to check:

[scholar@studentvm1 testdir]$ File="TestFile1" ; contact $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; else echo "$File doesn't exist or is empty." ; fi
Take a look atFile1 doesn't exist or is empty.

Add some content material to the file and take a look at once more:

[scholar@studentvm1 testdir]$ File="TestFile1" ; echo "This is file $File" > $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; else echo "$File doesn't exist or is empty." ; fi
Take a look atFile1 exists and comprises information.

Now, add the elif stanza to discriminate between a file that doesn’t exist and one that’s empty:

[scholar@studentvm1 testdir]$ File="TestFile1" ; contact $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File doesn't exist." ; fi
Take a look atFile1 exists and is empty.
[scholar@studentvm1 testdir]$ File="TestFile1" ; echo "This is $File" > $File ; if [ -s $File ] ; then echo "$File exists and comprises information." ; elif [ -e $File ] ; then echo "$File exists and is empty." ; else echo "$File doesn't exist." ; fi
Take a look atFile1 exists and comprises information.
[scholar@studentvm1 testdir]$

Now you’ve got a Bash CLI program that may take a look at for these three totally different circumstances… however the prospects are infinite.

It is simpler to see the logic construction of the extra advanced compound instructions for those who organize this system statements extra such as you would in a script that you could save in a file. Figure 2 reveals how this might look. The indents of this system statements in every stanza of the if-elif-else construction assist to make clear the logic.

File="TestFile1"
echo "This is $File" > $File
if [ -s $File ]
   then
   echo "$File exists and contains data."
elif [ -e $File ]
   then
   echo "$File exists and is empty."
else
   echo "$File does not exist."
fi

Fig. 2: The command line program rewritten as it could seem in a script

Logic this advanced is just too prolonged for many CLI packages. Although any Linux or Bash built-in instructions could also be utilized in CLI packages, because the CLI packages get longer and extra advanced, it makes extra sense to create a script that’s saved in a file and may be executed at any time, now or sooner or later.

String comparability operators

String comparability operators allow the comparability of alphanumeric strings of characters. There are just a few of those operators, that are listed in Figure three.

Operator Description
-z string True if the size of string is zero
-n string True if the size of string is non-zero
string1 == string2
or
string1 = string2
True if the strings are equal; a single = must be used with the take a look at command for POSIX conformance. When used with the [[ command, this performs sample matching as described above (compound instructions).
string1 != string2 True if the strings aren’t equal
string1 < string2 True if string1 types earlier than string2 lexicographically (refers to locale-specific sorting sequences for all alphanumeric and particular characters)
string1 > string2 True if string1 types after string2 lexicographically

Fig. three: Bash string logical operators

First, take a look at string size. The quotes round $MyVar within the comparability should be there for the comparability to work. (You ought to nonetheless be working in ~/testdir.)

[scholar@studentvm1 testdir]$ MyVar="" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero size.
[scholar@studentvm1 testdir]$ MyVar="Random text" ; if [ -z "" ] ; then echo "MyVar is zero length." ; else echo "MyVar contains data" ; fi
MyVar is zero size.

You may additionally do it this fashion:

[scholar@studentvm1 testdir]$ MyVar="Random text" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar comprises information.
[scholar@studentvm1 testdir]$ MyVar="" ; if [ -n "$MyVar" ] ; then echo "MyVar contains data." ; else echo "MyVar is zero length" ; fi
MyVar is zero size

Sometimes it’s possible you’ll have to know a string’s actual size. This shouldn’t be a comparability, however it’s associated. Unfortunately, there isn’t a easy option to decide the size of a string. There are a few methods to do it, however I feel utilizing the expr (consider expression) command is best. Read the person web page for expr for extra about what it might do. Note that quotes are required across the string or variable you are testing.

[scholar@studentvm1 testdir]$ MyVar="" ; expr size "$MyVar"
zero
[scholar@studentvm1 testdir]$ MyVar="How long is this?" ; expr size "$MyVar"
17
[scholar@studentvm1 testdir]$ expr size "We can also find the length of a literal string as well as a variable."
70

Regarding comparability operators, I take advantage of a number of testing in my scripts to find out whether or not two strings are equal (i.e., similar). I take advantage of the non-POSIX model of this comparability operator:

[scholar@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello World" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 matches Var2
[scholar@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello world" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches Var2" ; else echo "Var1 and Var2 do not match." ; fi
Var1 and Var2 do not match.

Experiment some extra by yourself to check out these operators.

Numeric comparability operators

Numeric operators make comparisons between two numeric arguments. Like the opposite operator courses, most are simple to grasp.

Operator Description
arg1 -eq arg2 True if arg1 equals arg2
arg1 -ne arg2 True if arg1 shouldn’t be equal to arg2
arg1 -lt arg2 True if arg1 is lower than arg2
arg1 -le arg2 True if arg1 is lower than or equal to arg2
arg1 -gt arg2 True if arg1 is bigger than arg2
arg1 -ge arg2 True if arg1 is bigger than or equal to arg2

Fig. four: Bash numeric comparability logical operators

Here are some easy examples. The first occasion units the variable $X to 1, then checks to see if $X is the same as 1. In the second occasion, X is about to zero, so the comparability shouldn’t be true.

[scholar@studentvm1 testdir]$ X=1 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X equals 1
[scholar@studentvm1 testdir]$ X=zero ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X doesn't equal 1
[scholar@studentvm1 testdir]$

Try some extra experiments by yourself.

Miscellaneous operators

These miscellaneous operators present whether or not a shell possibility is about or a shell variable has a worth, however it doesn’t uncover the worth of the variable, simply whether or not it has one.

Operator Description
-o optname True if the shell possibility optname is enabled (see the record of choices beneath the outline of the -o choice to the Bash set builtin within the Bash man web page)
-v varname True if the shell variable varname is about (has been assigned a worth)
-R varname True if the shell variable varname is about and is a reputation reference

Fig. 5: Miscellaneous Bash logical operators

Experiment by yourself to check out these operators.

Expansions

Bash helps a variety of forms of expansions and substitutions that may be fairly helpful. According to the Bash man web page, Bash has seven types of expansions. This article seems to be at 5 of them: tilde growth, arithmetic growth, pathname growth, brace growth, and command substitution.

Brace growth

Brace growth is a technique for producing arbitrary strings. (This software is used beneath to create numerous information for experiments with particular sample characters.) Brace growth can be utilized to generate lists of arbitrary strings and insert them into a particular location inside an enclosing static string or at both finish of a static string. This could also be exhausting to visualise, so it is best to simply do it.

First, this is what a brace growth does:

[scholar@studentvm1 testdir]$ echo
string1 string2 string3

Well, that’s not very useful, is it? But look what occurs once you use it only a bit in a different way:

[scholar@studentvm1 testdir]$ echo "Hello ".
Hello David. Hello Jen. Hello Rikki. Hello Jason.

That seems to be like one thing helpful—it may save a great deal of typing. Now do this:

[scholar@studentvm1 testdir]$ echo bs
beds bolts bars

I may go on, however you get the thought.

Tilde growth

Arguably, the most typical growth is the tilde (~) growth. When you utilize this in a command like cd ~/Documents, the Bash shell expands it as a shortcut to the person’s full house listing.

Use these Bash packages to look at the results of the tilde growth:

[scholar@studentvm1 testdir]$ echo ~
/house/scholar
[scholar@studentvm1 testdir]$ echo ~/Documents
/house/scholar/Documents
[scholar@studentvm1 testdir]$ Var1=~/Documents ; echo $Var1 ; cd $Var1
/house/scholar/Documents
[scholar@studentvm1 Documents]$

Pathname growth

Pathname growth is a elaborate time period increasing file-globbing patterns, utilizing the characters ? and *, into the complete names of directories that match the sample. File globbing refers to particular sample characters that allow vital flexibility in matching file names, directories, and different strings when performing numerous actions. These particular sample characters enable matching single, a number of, or particular characters in a string.

  • ? — Matches solely considered one of any character within the specified location inside the string
  • * — Matches zero or extra of any character within the specified location inside the string

This growth is utilized to matching listing names. To see how this works, be certain that testdir is the current working listing (PWD) and begin with a plain itemizing (the contents of my house listing might be totally different from yours):

[scholar@studentvm1 testdir]$ ls
chapter6  cpuHog.dos    dmesg1.txt  Documents  Music       softlink1  testdir6    Videos
chapter7  cpuHog.Linux  dmesg2.txt  Downloads  Pictures    Templates  testdir
testdir  cpuHog.mac    dmesg3.txt  file005    Public      testdir    tmp
cpuHog     Desktop       dmesg.txt   link3      random.txt  testdir1   umask.take a look at
[scholar@studentvm1 testdir]$

Now record the directories that begin with Do, testdir/Documents, and testdir/Downloads:

Documents:
Listing01  file07  file15        take a look at02  take a look at10  take a look at20      testfile13  Textual contentFiles
Listing02  file08  file16        take a look at03  take a look at11  testfile01  testfile14
file01       file09  file17        take a look at04  take a look at12  testfile04  testfile15
file02       file10  file18        take a look at05  take a look at13  testfile05  testfile16
file03       file11  file19        take a look at06  take a look at14  testfile09  testfile17
file04       file12  file20        take a look at07  take a look at15  testfile10  testfile18
file05       file13  Student1.txt  take a look at08  take a look at16  testfile11  testfile19
file06       file14  take a look at01        take a look at09  take a look at18  testfile12  testfile20

Downloads:
[scholar@studentvm1 testdir]$

Well, that didn’t do what you needed. It listed the contents of the directories that start with Do. To record solely the directories and never their contents, use the -d possibility.

[scholar@studentvm1 testdir]$ ls -d Do*
Documents  Downloads
[scholar@studentvm1 testdir]$

In each instances, the Bash shell expands the Do* sample into the names of the 2 directories that match the sample. But what if there are additionally information that match the sample?

[scholar@studentvm1 testdir]$ contact Downtown ; ls -d Do*
Documents  Downloads  Downtown
[scholar@studentvm1 testdir]$

This reveals the file, too. So any information that match the sample are additionally expanded to their full names.

Command substitution

Command substitution is a type of growth that permits the STDOUT information stream of 1 command for use because the argument of one other command; for instance, as an inventory of things to be processed in a loop. The Bash man web page says: “Command substitution allows the output of a command to replace the command name.” I discover that to be correct if a bit obtuse.

There are two types of this substitution, `command` and $(command). In the older type utilizing again tics (`), utilizing a backslash () within the command retains its literal that means. However, when it is used within the newer parenthetical type, the backslash takes on its that means as a particular character. Note additionally that the parenthetical type makes use of solely single parentheses to open and shut the command assertion.

I steadily use this functionality in command-line packages and scripts the place the outcomes of 1 command can be utilized as an argument for one more command.

Start with a quite simple instance that makes use of each types of this growth (once more, be certain that testdir is the PWD):

[scholar@studentvm1 testdir]$ echo "Todays date is `date`"
Todays date is Sun Apr  7 14:42:46 EDT 2019
[scholar@studentvm1 testdir]$ echo "Todays date is $(date)"
Todays date is Sun Apr  7 14:42:59 EDT 2019
[scholar@studentvm1 testdir]$

The -w choice to the seq utility provides main zeros to the numbers generated in order that they’re all the identical width, i.e., the identical variety of digits whatever the worth. This makes it simpler to type them in numeric sequence.

The seq utility is used to generate a sequence of numbers:

[scholar@studentvm1 testdir]$ seq 5
1
2
three
four
5
[scholar@studentvm1 testdir]$ echo `seq 5`
1 2 three four 5
[scholar@studentvm1 testdir]$

Now you are able to do one thing a bit extra helpful, like creating numerous empty information for testing:

[scholar@studentvm1 testdir]$ for I in $(seq -w 5000) ; do contact file-$I ; finished

In this utilization, the assertion seq -w 5000 generates an inventory of numbers from one to five,000. By utilizing command substitution as a part of the for assertion, the record of numbers is utilized by the for assertion to generate the numerical a part of the file names.

Arithmetic growth

Bash can carry out integer math, however it’s fairly cumbersome (as you’ll quickly see). The syntax for arithmetic growth is $((arithmetic-expression)), utilizing double parentheses to open and shut the expression.

Arithmetic growth works like command substitution in a shell program or script; the worth calculated from the expression replaces the expression for additional analysis by the shell.

Once once more, begin with one thing easy:

[scholar@studentvm1 testdir]$ echo $((1+1))
2
[scholar@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Varthree=$((Var1*Var2)) ; echo "Var three = $Varthree"
Var three = 35

The following division leads to zero as a result of the outcome could be a decimal worth of lower than one:

[scholar@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Varthree=$((Var1/Var2)) ; echo "Var three = $Varthree"
Var three = zero

Here is an easy calculation I usually do in a script or CLI program that tells me how a lot complete digital reminiscence I’ve in a Linux host. The free command doesn’t present that information:

[scholar@studentvm1 testdir]$ RAM=`free | grep ^Mem | awk ''` ; Swap=`free | grep ^Swap | awk ''` ; echo "RAM = $RAM and Swap = $Swap" ; echo "Total Virtual reminiscence is $((RAM+Swap))" ;
RAM = 4037080 and Swap = 6291452
Total Virtual reminiscence is 10328532

I used the ` character to delimit the sections of code used for command substitution.

I take advantage of Bash arithmetic growth principally for checking system useful resource quantities in a script after which select a program execution path based mostly on the outcome.

Summary

This article, the second on this sequence on Bash as a programming language, explored the Bash file, string, numeric, and miscellaneous logical operators that present execution-flow management logic and the various kinds of shell expansions.

The third article on this sequence will discover the usage of loops for performing numerous forms of iterative operations.

Most Popular

To Top