An Introduction To Programming With Bash
An Introduction To Programming With Bash
com
An introduction to
programming with Bash
Opensource.com . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
About Opensource.com
What is Opensource.com?
David Both
Email: LinuxGeek46@both.org
Twitter: @LinuxGeek46
Chapters
Loops 15
My objective when writing CLI programs is to save time That may not seem like much of a program, but it is the first
and effort (i.e., to be the lazy sysadmin). CLI programs program I encounter with every new programming language
support this by listing several commands in a specific se- I learn. The syntax may be a bit different for each language,
quence that execute one after another, so you do not need but the result is the same.
to watch the progress of one command and type in the Let’s expand a little on this trivial but ubiquitous program.
next command when the first finishes. You can go do other Your results will be different from mine because I have done
things and not have to continually monitor the progress of other experiments, while you may have only the default
each command. directories and files that are created in the account home
directory the first time you log into an account via the GUI
What is “a program”? desktop.
The Free On-line Dictionary of Computing (FOLDOC) [5]
defines a program as: “The instructions executed by a com- [student@studentvm1 ~]$ echo "My home directory." ; ls ;
puter, as opposed to the physical device on which they run.” My home directory.
Princeton University’s WordNet [6] defines a program as: ch
apter25 TestFile1.Linux dmesg2.txt Downloads newfile.txt
“…a sequence of instructions that a computer can interpret softlink1 testdir6
and execute…” Wikipedia [7] also has a good entry about ch
apter26 TestFile1.mac dmesg3.txt file005 Pictures
computer programs. Templates testdir
Therefore, a program can consist of one or more instructions Te
stFile1 Desktop dmesg.txt link3 Public
that perform a specific, related task. A computer program in- testdir Videos
struction is also called a program statement. For sysadmins, Te
stFile1.dos dmesg1.txt Documents Music random.txt
a program is usually a sequence of shell commands. All the testdir1
shells available for Linux, at least the ones I am familiar with,
have at least a basic form of programming capability, and Bash, That makes a bit more sense. The results are related, but
the default shell for most Linux distributions, is no exception. the individual program statements are independent of each
While this guide uses Bash (because it is so ubiquitous), if other. Notice that I like to put spaces before and after the
you use a different shell, the general programming concepts semicolon because it makes the code a bit easier to read.
will be the same, although the constructs and syntax may dif- Try that little CLI program again without an explicit semicolon
fer somewhat. Some shells may support some features that at the end:
others do not, but they all provide some programming capa-
bility. Shell programs can be stored in a file for repeated use, [student@studentvm1 ~]$ echo "My home directory." ; ls
or they may be created on the command line as needed.
There is no difference in the output.
Simple CLI programs
The simplest command-line programs are one or two con- Something about variables
secutive program statements, which may be related or not, Like all programming languages, the Bash shell can deal
that are entered on the command line before the Enter key with variables. A variable is a symbolic name that refers
is pressed. The second statement in a program, if there is to a specific location in memory that contains a value of
one, might be dependent upon the actions of the first, but it some sort. The value of a variable is changeable, i.e., it
does not need to be. is variable.
There is also one bit of syntactical punctuation that needs Bash does not type variables like C and related languag-
to be clearly stated. When entering a single command on the es, defining them as integers, floating points, or string types.
command line, pressing the Enter key terminates the com- In Bash, all variables are strings. A string that is an integer
mand with an implicit semicolon (;). When used in a CLI shell can be used in integer arithmetic, which is the only type of
program entered as a single line on the command line, the math that Bash is capable of doing. If more complex math
semicolon must be used to terminate each statement and is required, the bc command can be used in CLI programs
separate it from the next one. The last statement in a CLI and scripts.
shell program can use an explicit or implicit semicolon. Variables are assigned values and can be used to refer to
those values in CLI programs and scripts. The value of a vari-
Some basic syntax able is set using its name but not preceded by a $ sign. The
The following examples will clarify this syntax. This program assignment VAR=10 sets the value of the variable VAR to 10.
consists of a single command with an explicit terminator: To print the value of the variable, you can use the statement
echo $VAR. Start with text (i.e., non-numeric) variables.
[student@studentvm1 ~]$ echo "Hello world." ; Bash variables become part of the shell environment until
Hello world. they are unset.
Check the initial value of a variable that has not been as- Those commands all run without a problem so long as no
signed; it should be null. Then assign a value to the variable errors occur. But what happens when an error occurs?
and print it to verify its value. You can do all of this in a single You can anticipate and allow for errors using the built-in
CLI program: && and || Bash control operators. These two control op-
erators provide some flow control and enable you to alter
[student@studentvm1 ~]$ echo $MyVar ; MyVar="Hello World" ; the sequence of code execution. The semicolon is also
echo $MyVar ; considered to be a Bash control operator, as is the new-
line character.
Hello World The && operator simply says, “if command1 is successful,
[student@studentvm1 ~]$ then run command2. If command1 fails for any reason, then
command2 is skipped.” That syntax looks like this:
Note: The syntax of variable assignment is very strict. There
must be no spaces on either side of the equal (=) sign in the command1 && command2
assignment statement.
The empty line indicates that the initial value of MyVar is null. Now, look at some commands that will create a new direc-
Changing and setting the value of a variable are done the same tory and—if it’s successful—make it the present working di-
way. This example shows both the original and the new value. rectory (PWD). Ensure that your home directory (~) is the
As mentioned, Bash can perform integer arithmetic cal- PWD. Try this first in /root, a directory that you do not have
culations, which is useful for calculating a reference to the access to:
location of an element in an array or doing simple math prob-
lems. It is not suitable for scientific computing or anything [student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
that requires decimals, such as financial calculations. There mkdir: cannot create directory '/root/testdir/': Permission denied
are much better tools for those types of calculations. [student@studentvm1 ~]$
Here’s a simple calculation:
The error was emitted by the mkdir command. You did not
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = receive an error indicating that the file could not be created
$((Var1*Var2))" because the creation of the directory failed. The && con-
Result = 63 trol operator sensed the non-zero return code, so the touch
command was skipped. Using the && control operator pre-
What happens when you perform a math operation that re- vents the touch command from running because there was
sults in a floating-point number? an error in creating the directory. This type of command-line
program flow control can prevent errors from compounding
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = and making a real mess of things. But it’s time to get a little
$((Var1/Var2))" more complicated.
Result = 0 The || control operator allows you to add another program
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = statement that executes when the initial program statement re-
$((Var2/Var1))" turns a code greater than zero. The basic syntax looks like this:
Result = 1
[student@studentvm1 ~]$ command1 || command2
The result is the nearest integer. Notice that the calculation This syntax reads, “If command1 fails, execute command2.”
was performed as part of the echo statement. The math is That implies that if command1 succeeds, command2 is
performed before the enclosing echo command due to the skipped. Try this by attempting to create a new directory:
Bash order of precedence. For details see the Bash man
page and search “precedence.” [student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir || echo
"$Dir was not created."
Control operators mk
dir: cannot create directory '/root/testdir': Permission
Shell control operators are one of the syntactical oper- denied
ators for easily creating some interesting command-line /root/testdir was not created.
programs. The simplest form of CLI program is just string- [student@studentvm1 ~]$
ing several commands together in a sequence on the
command line: This is exactly what you would expect. Because the new di-
rectory could not be created, the first command failed, which
command1 ; command2 ; command3 ; command4 ; . . . ; etc. ; resulted in the execution of the second command.
Combining these two operators provides the best of [student@studentvm1 testdir]$ ll ; echo "RC = $?"
both. The control operator syntax using some flow control total 1264
takes this general form when the && and || control oper- drwxrwxr-x 2 student student 4096 Mar 2 08:21 chapter25
ators are used: drwxrwxr-x 2 student student 4096 Mar 21 15:27 chapter26
-rwxr-xr-x 1 student student 92 Mar 20 15:53 TestFile1
pr
eceding commands ; command1 && command2 || command3 ; <snip>
following commands drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student 4096 Dec 22 13:15 Videos
This syntax can be stated like so: “If command1 exits with a RC = 0
return code of 0, then execute command2, otherwise exe- [student@studentvm1 testdir]$
cute command3.” Try it:
The RC, in this case, is zero, which means the command
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir && cd completed successfully. Now try the same command on
$Dir || echo "$Dir was not created." root’s home directory, a directory you do not have permis-
mk
dir: cannot create directory '/root/testdir': Permission sions for:
denied
/root/testdir was not created. [student@studentvm1 testdir]$ ll /root ; echo "RC = $?"
[student@studentvm1 ~]$ ls: cannot open directory '/root': Permission denied
RC = 2
Now try the last command again using your home directory [student@studentvm1 testdir]$
instead of the /root directory. You will have permission to
create this directory: In this case, the RC is two; this means permission was de-
nied for a non-root user to access a directory to which the
[s
tudent@studentvm1 ~]$ Dir=~/testdir ; mkdir $Dir && cd $Dir user is not permitted access. The control operators use
|| echo "$Dir was not created." these RCs to enable you to alter the sequence of program
[student@studentvm1 testdir]$ execution.
Logical operators
and shell expansions
Learn about logical operators and shell expansions, in the second
part in this three-part guide on programming with Bash.
The spaces in the comparison are required as shown. The -k filename True if the file exists and its “sticky’” bit
is set
single square braces, [ and ], are the traditional Bash sym-
bols that are equivalent to the test command: -p filename True if the file exists and is a named pipe
(FIFO)
if test arg1 operator arg2 ; then list -r filename True if the file exists and is readable, i.e.,
has its read bit set
There is also a more recent syntax that offers a few advan- -s filename True if the file exists and has a size great-
tages and that some sysadmins prefer. This format is a bit er than zero; a file that exists but that has
less compatible with different versions of Bash and other a size of zero will return false
shells, such as ksh (the Korn shell). It looks like: -t fd True if the file descriptor fd is open and
refers to a terminal
if [[ arg1 operator arg2 ]] ; then list
-u filename True if the file exists and its set-user-id
bit is set
File operators
-w filename True if the file exists and is writable
File operators are a powerful set of logical operators within
Bash. Figure 1 lists more than 20 different operators that -x filename True if the file exists and is executable
-G filename True if the file exists and is owned by the [student@studentvm1 testdir]$ File="TestFile1" ; echo "This
effective group ID is file $File" > $File ; if [ -s $File ] ; then echo "$File
-L filename True if the file exists and is a symbolic link exists and contains data." ; fi
-N filename True if the file exists and has been modi- TestFile1 exists and contains data.
fied since it was last read [student@studentvm1 testdir]$
-O filename True if the file exists and is owned by the
effective user ID That works, but it is only truly accurate for one specific con-
-S filename True if the file exists and is a socket dition out of the three possible ones. Add an else stanza so
file1 -ef file2 True if file1 and file2 refer to the same you can be somewhat more accurate, and delete the file so
device and iNode numbers you can fully test this new code:
file1 -nt file2 True if file1 is newer (according to mod-
ification date) than file2, or if file1 exists [student@studentvm1 testdir]$ File="TestFile1" ; rm $File ; if
and file2 does not [ -s $File ] ; then echo "$File exists and contains data." ;
file1 -ot file2 True if file1 is older than file2, or if file2 else echo "$File does not exist or is empty." ; fi
exists and file1 does not TestFile1 does not exist or is empty.
Fig. 1: The Bash file operators Now create an empty file to test:
As an example, start by testing for the existence of a file:
[student@studentvm1 testdir]$ File="TestFile1" ; touch $File ;
[student@studentvm1 testdir]$ File="TestFile1" ; if [ -e $File ] if [ -s $File ] ; then echo "$File exists and contains data." ;
; then echo "The file $File exists." ; else echo "The file $File else echo "$File does not exist or is empty." ; fi
does not exist." ; fi TestFile1 does not exist or is empty.
The file TestFile1 does not exist.
[student@studentvm1 testdir]$ Add some content to the file and test again:
Next, create a file for testing named TestFile1. For now, it [student@studentvm1 testdir]$ File="TestFile1" ; echo "This
does not need to contain any data: is file $File" > $File ; if [ -s $File ] ; then echo "$File
exists and contains data." ; else echo "$File does not exist
[student@studentvm1 testdir]$ touch TestFile1 or is empty." ; fi
TestFile1 exists and contains data.
It is easy to change the value of the $File variable rather
than a text string for the file name in multiple locations in this Now, add the elif stanza to discriminate between a file that
short CLI program: does not exist and one that is empty:
Operator Description 0
[student@studentvm1 testdir]$ MyVar="How long is this?" ; expr
-z string True if the length of string is zero
length "$MyVar"
-n string True if the length of string is non-zero 17
string1 == string2 True if the strings are equal; a single [student@studentvm1 testdir]$ expr length "We can also find the
or = should be used with the test com- length of a literal string as well as a variable."
string1 = string2 mand for POSIX conformance. When 70
used with the [[ command, this per-
forms pattern matching as described Regarding comparison operators, I use a lot of testing
above (compound commands). in my scripts to determine whether two strings are equal
string1 != string2 True if the strings are not equal (i.e., identical). I use the non-POSIX version of this com-
parison operator:
string1 < string2 True if string1 sorts before string2 lex-
icographically (refers to locale-specif-
[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello
ic sorting sequences for all alphanu-
World" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches
meric and special characters)
Var2" ; else echo "Var1 and Var2 do not match." ; fi
string1 > string2 True if string1 sorts after string2 lexi- Va
r1 matches Var2
cographically
[student@studentvm1 testdir]$ Var1="Hello World" ; Var2="Hello
Fig. 3: Bash string logical operators world" ; if [ "$Var1" == "$Var2" ] ; then echo "Var1 matches
Var2" ; else echo "Var1 and Var2 do not match." ; fi
First, look at string length. The quotes around $MyVar in the Va
r1 and Var2 do not match.
comparison must be there for the comparison to work. (You
should still be working in ~/testdir.) Experiment some more on your own to try out these operators.
Operator Description
expansion can be used to generate lists of arbitrary strings
arg1 -eq arg2 True if arg1 equals arg2 and insert them into a specific location within an enclosing
arg1 -ne arg2 True if arg1 is not equal to arg2 static string or at either end of a static string. This may be
arg1 -lt arg2 True if arg1 is less than arg2 hard to visualize, so it’s best to just do it.
First, here’s what a brace expansion does:
arg1 -le arg2 True if arg1 is less than or equal to arg2
arg1 -gt arg2 True if arg1 is greater than arg2 [student@studentvm1 testdir]$ echo {string1,string2,string3}
arg1 -ge arg2 True if arg1 is greater than or equal to arg2 string1 string2 string3
This expansion is applied to matching directory names. [student@studentvm1 testdir]$ touch Downtown ; ls -d Do*
To see how this works, ensure that testdir is the pres- Documents Downloads Downtown
ent working directory (PWD) and start with a plain listing [student@studentvm1 testdir]$
(the contents of my home directory will be different from
yours): This shows the file, too. So any files that match the pattern
are also expanded to their full names.
[student@studentvm1 testdir]$ ls
ch
apter6 cpuHog.dos dmesg1.txt Documents Music Command substitution
softlink1 testdir6 Videos Command substitution is a form of expansion that allows the
ch
apter7 cpuHog.Linux dmesg2.txt Downloads Pictures STDOUT data stream of one command to be used as the ar-
Templates testdir gument of another command; for example, as a list of items
te
stdir cpuHog.mac dmesg3.txt file005 Public to be processed in a loop. The Bash man page says: “Com-
testdir tmp mand substitution allows the output of a command to replace
cp
uHog Desktop dmesg.txt link3 random.txt the command name.” I find that to be accurate if a bit obtuse.
testdir1 umask.test There are two forms of this substitution, `command` and
[student@studentvm1 testdir]$ $(command). In the older form using back tics (`), using
a backslash (\) in the command retains its literal meaning.
Now list the directories that start with Do, testdir/Docu- However, when it’s used in the newer parenthetical form, the
ments, and testdir/Downloads: backslash takes on its meaning as a special character. Note
also that the parenthetical form uses only single parentheses
Documents: to open and close the command statement.
Di
rectory01 file07 file15 test02 test10 test20 I frequently use this capability in command-line programs
testfile13 TextFiles and scripts where the results of one command can be used
Di
rectory02 file08 file16 test03 test11 testfile01 as an argument for another command.
testfile14 Start with a very simple example that uses both forms of
fil
e01 file09 file17 test04 test12 testfile04 this expansion (again, ensure that testdir is the PWD):
testfile15
fil
e02 file10 file18 test05 test13 testfile05 [student@studentvm1 testdir]$ echo "Todays date is `date`"
testfile16 Todays date is Sun Apr 7 14:42:46 EDT 2019
fil
e03 file11 file19 test06 test14 testfile09 [student@studentvm1 testdir]$ echo "Todays date is $(date)"
testfile17 Todays date is Sun Apr 7 14:42:59 EDT 2019
fil
e04 file12 file20 test07 test15 testfile10 [student@studentvm1 testdir]$
testfile18
fil
e05 file13 Student1.txt test08 test16 testfile11 The -w option to the seq utility adds leading zeros to the
testfile19 numbers generated so that they are all the same width,
file06 file14 test01 test09 test18 testfile12 i.e., the same number of digits regardless of the value. This
testfile20 makes it easier to sort them in numeric sequence.
The seq utility is used to generate a sequence of numbers:
Downloads:
[student@studentvm1 testdir]$ [student@studentvm1 testdir]$ seq 5
1
Well, that did not do what you wanted. It listed the contents of 2
the directories that begin with Do. To list only the directories 3
and not their contents, use the -d option. 4
5
[student@studentvm1 testdir]$ ls -d Do* [student@studentvm1 testdir]$ echo `seq 5`
Documents Downloads 1 2 3 4 5
[student@studentvm1 testdir]$ [student@studentvm1 testdir]$
In both cases, the Bash shell expands the Do* pattern Now you can do something a bit more useful, like creating a
into the names of the two directories that match the large number of empty files for testing:
pattern. But what if there are also files that match the
pattern? [s
tudent@studentvm1 testdir]$ for I in $(seq -w 5000) ; do
touch file-$I ; done
In this usage, the statement seq -w 5000 generates a list Here is a simple calculation I often do in a script or CLI
of numbers from one to 5,000. By using command substi- program that tells me how much total virtual memory I
tution as part of the for statement, the list of numbers is have in a Linux host. The free command does not provide
used by the for statement to generate the numerical part that data:
of the file names.
[student@studentvm1 testdir]$ RAM=`free | grep ^Mem | awk
Arithmetic expansion '{print $2}'` ; Swap=`free | grep ^Swap | awk '{print $2}'`
Bash can perform integer math, but it is rather cumbersome ; echo "RAM = $RAM and Swap = $Swap" ; echo "Total Virtual
(as you will soon see). The syntax for arithmetic expansion memory is $((RAM+Swap))" ;
is $((arithmetic-expression)), using double parentheses RA
M = 4037080 and Swap = 6291452
to open and close the expression. Total Virtual memory is 10328532
Arithmetic expansion works like command substitution
in a shell program or script; the value calculated from the I used the ` character to delimit the sections of code used for
expression replaces the expression for further evaluation command substitution.
by the shell. I use Bash arithmetic expansion mostly for checking sys-
Once again, start with something simple: tem resource amounts in a script and then choose a program
execution path based on the result.
[student@studentvm1 testdir]$ echo $((1+1))
2 Summary
[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; This part, the second in this guide on Bash as a program-
Var3=$((Var1*Var2)) ; echo "Var 3 = $Var3" ming language, explored the Bash file, string, numeric,
Var 3 = 35 and miscellaneous logical operators that provide exe-
cution-flow control logic and the different types of shell
The following division results in zero because the result expansions.
would be a decimal value of less than one: The third part in this guide will explore the use of loops
for performing various types of iterative operations.
[student@studentvm1 testdir]$ Var1=5 ; Var2=7 ; Var3=$((Var1/
Var2)) ; echo "Var 3 = $Var3"
Var 3 = 0
Loops
Learn how to use loops for performing iterative operations, in the
final part in this three-part guide on programming with Bash.
for Var in list1 ; do list2 ; done [student@studentvm1 testdir]$ for Dept in "Human Resources"
Sales Finance "Information Technology" Engineering
This translates to: “For each value in list1, set the $Var to Administration Research ; do echo "Working on Department
that value and then perform the program statements in list2 $Dept" ; mkdir "$Dept" ; done
using that value; when all of the values in list1 have been Working on Department Human Resources
used, it is finished, so exit the loop.” The values in list1 can Working on Department Sales
be a simple, explicit string of values, or they can be the result Working on Department Finance
of a command substitution (described in the second part in Working on Department Information Technology
the guide). I use this construct frequently. Working on Department Engineering
To try it, ensure that ~/testdir is still the present working Working on Department Administration
directory (PWD). Clean up the directory, then look at a trivial Working on Department Research
example of the for loop starting with an explicit list of values. [student@studentvm1 testdir]$ ll
This list is a mix of alphanumeric values—but do not forget total 28
that all variables are strings and can be treated as such. drwxrwxr-x 2 student student 4096 Apr 8 15:45 Administration
drwxrwxr-x 2 student student 4096 Apr 8 15:45 Engineering
[student@studentvm1 testdir]$ rm * drwxrwxr-x 2 student student 4096 Apr 8 15:45 Finance
[student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Human Resources'
echo $I ; done drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Information
a Technology'
b drwxrwxr-x 2 student student 4096 Apr 8 15:45 Research
c drwxrwxr-x 2 student student 4096 Apr 8 15:45 Sales
d
1 The $Dept variable must be enclosed in quotes in the mkdir
2 statement; otherwise, two-part department names (such as
3 “Information Technology”) will be treated as two separate de-
4 partments. That highlights a best practice I like to follow: all
file and directory names should be a single word. Although
Here is a bit more useful version with a more meaningful most modern operating systems can deal with spaces in
variable name: names, it takes extra work for sysadmins to ensure that
those special cases are considered in scripts and CLI pro- rpcbind-1.2.5-0.fc29.x86_64
grams. (They almost certainly should be considered, even libsss_sudo-2.0.0-5.fc29.x86_64
if they’re annoying because you never know what files you libfontenc-1.1.3-9.fc29.x86_64
will have.) <snip>
So, delete everything in ~/testdir—again—and do this
one more time: Add the sort and uniq commands to sort the list and print
the unique ones (since it’s possible that some RPMs with
[student@studentvm1 testdir]$ rm -rf * ; ll identical names are installed):
total 0
[student@studentvm1 testdir]$ for Dept in Human-Resources Sales [student@studentvm1 testdir]$ rpm -qa | sort | uniq
Finance Information-Technology Engineering Administration a2ps-4.14-39.fc29.x86_64
Research ; do echo "Working on Department $Dept" ; mkdir aajohan-comfortaa-fonts-3.001-3.fc29.noarch
"$Dept" ; done abattis-cantarell-fonts-0.111-1.fc29.noarch
Working on Department Human-Resources abiword-3.0.2-13.fc29.x86_64
Working on Department Sales abrt-2.11.0-1.fc29.x86_64
Working on Department Finance abrt-addon-ccpp-2.11.0-1.fc29.x86_64
Working on Department Information-Technology abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64
Working on Department Engineering abrt-addon-kerneloops-2.11.0-1.fc29.x86_64
Working on Department Administration abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64
Working on Department Research abrt-addon-vmcore-2.11.0-1.fc29.x86_64
[student@studentvm1 testdir]$ ll <snip>
total 28
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Administration Since this gives the correct list of RPMs you want to look at,
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Engineering you can use this as the input list to a loop that will print all the
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Finance details of each RPM:
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Human-Resources
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Information- [student@studentvm1 testdir]$ for RPM in `rpm -qa | sort |
Technology uniq` ; do rpm -qi $RPM ; done
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Research
drwxrwxr-x 2 student student 4096 Apr 8 15:52 Sales This code produces way more data than you want. Note that
the loop is complete. The next step is to extract only the in-
Suppose someone asks for a list of all RPMs on a partic- formation the PHBs requested. So, add an egrep command,
ular Linux computer and a short description of each. This which is used to select ^Name or ^Summary. The carat (^)
happened to me when I worked for the State of North Caro- specifies the beginning of the line; thus, any line with Name
lina. Since open source was not “approved” for use by state or Summary at the beginning of the line is displayed.
agencies at that time, and I only used Linux on my desktop
computer, the pointy-haired bosses (PHBs) needed a list of
each piece of software that was installed on my computer so [student@studentvm1 testdir]$ for RPM in `rpm -qa | sort |
that they could “approve” an exception. uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary"
How would you approach that? Here is one way, starting Name : a2ps
with the knowledge that the rpm –qa command provides a Summary : Converts text and other types of files to
complete description of an RPM, including the two items the PostScript
PHBs want: the software name and a brief summary. Name : aajohan-comfortaa-fonts
Build up to the final result one step at a time. First, list Summary : Modern style true type font
all RPMs: Name : abattis-cantarell-fonts
Summary : Humanist sans serif font
[student@studentvm1 testdir]$ rpm -qa Name : abiword
perl-HTTP-Message-6.18-3.fc29.noarch Summary : Word processing program
perl-IO-1.39-427.fc29.x86_64 Name : abrt
perl-Math-Complex-1.59-429.fc29.noarch Summary : Automatic bug detection and reporting tool
lua-5.3.5-2.fc29.x86_64 <snip>
java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64
util-linux-2.32.1-1.fc29.x86_64 You can try grep instead of egrep in the command above,
libreport-fedora-2.9.7-1.fc29.x86_64 but it will not work. You could also pipe the output of this
You have reduced two statements into a single one that It uses a logical comparison to count to a specific value:
prints the value of the variable and increments that value.
There is also a decrement operator, --. [student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo
You need a method for stopping the loop at a specific $((X++)) ; done
number. To accomplish that, change the true expression 0
to an actual numeric evaluation expression. Have the pro- 1
gram loop to 5 and stop. In the example code below, you 2
can see that -le is the logical numeric operator for “less 3
than or equal to.” This means: “So long as $X is less than 4
or equal to 5, the loop will continue. When $X increments to [student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo
6, the loop terminates.” $((++X)) ; done
1
[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo 2
$((X++)) ; done 3
0 4
1 5
2 [student@studentvm1 ~]$
3
4 Summary
5 This guide has explored many powerful tools for building
[student@studentvm1 ~]$ Bash command-line programs and shell scripts. But it
has barely scratched the surface on the many interesting
Until loop things you can do with Bash; the rest is up to you.
The until command is very much like the while command. I have discovered that the best way to learn Bash pro-
The difference is that it will continue to loop until the logical gramming is to do it. Find a simple project that requires
expression evaluates to “true.” Look at the simplest form of multiple Bash commands and make a CLI program out
this construct: of them. Sysadmins do many tasks that lend themselves
to CLI programming, so I am sure that you will easily find
[student@studentvm1 ~]$ X=0 ; until false ; do echo $((X++)) ; tasks to automate.
done | head Many years ago, despite being familiar with other shell
0 languages and Perl, I made the decision to use Bash for
1 all of my sysadmin automation tasks. I have discovered
2 that—sometimes with a bit of searching—I have been able
3 to use Bash to accomplish everything I need.
4
5 Links
6 [1] h
ttp://www.both.org/?page_id=1183
7 [2] h ttps://www.apress.com/us/book/9781484237298
8
9
[student@studentvm1 ~]$