{"id":3038,"date":"2023-12-24T02:30:00","date_gmt":"2023-12-24T07:30:00","guid":{"rendered":"https:\/\/www.both.org\/?p=3038"},"modified":"2024-01-21T11:24:18","modified_gmt":"2024-01-21T16:24:18","slug":"programming-bash-3-logical-operators","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=3038","title":{"rendered":"Programming Bash #3: Logical Operators"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"3038\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div>\n<p>In this series of articles we are looking at Bash as a programming language. In the second article, we explored some simple command line programming with Bash. We explored using variables and control operators. In this article we explore the five types of Bash file, string, numeric, and miscellaneous logical operators that provide execution flow control logic; different types of shell expansions.<\/p>\n\n\n\n<p>Logical operators are the basis for making decisions in a program and executing different sets of instructions based on those decisions. This is sometimes called flow control.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preparation<\/h2>\n\n\n\n<p>Before proceeding we need to create some test directories and files that will be used in this article. You should do this on a VM, a non-production computer, or a non-production account on your workstation. Make your home directory the PWD and execute the following instructions as a non-root user.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 ~]$ <strong>for I in 0 1 2 3 4 5 6 7 8 9 ; do echo \"This is testfile$I\" &gt; testfile$I.txt ; done<\/strong>\n&#91;dboth@testvm1 ~]$ <strong>mkdir -p .\/testdir1\/testdir2\/testdir3\/testdir4\/testdir5 testdir6 testdir7<\/strong>\n&#91;dboth@testvm1 ~]$ <strong>tree<\/strong>\n&#91;dboth@testvm1 ~]$ <strong>cd testdir1<\/strong>\n&#91;dboth@testvm1 ~]$ <strong>for X in dmesg.txt dmesg1.txt dmesg2.txt dmesg3.txt dmesg4.txt ; do dmesg &gt; $X ; done<\/strong>\n&#91;dboth@testvm1 testdir1]$ <strong>touch newfile.txt<\/strong>\n&#91;dboth@testvm1 testdir1]$ <strong>df -h &gt; diskusage.txt<\/strong>\n&#91;dboth@testvm1 testdir1]$ <strong>for I in 0 1 2 3 4 5 6 7 8 9 ; do dmesg &gt; file$I.txt ; done<\/strong><\/code><\/pre>\n\n\n\n<p>The -p option for the mkdir command creates the entire specified directory hierarchy if any of them don\u2019t exist. This prevents having to create each directory in the hierarchy individually. I use this fairly frequently and it is a great time-saver.<\/p>\n\n\n\n<p>This now gives us a few files and directories to work with.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Logical operators<\/h2>\n\n\n\n<p>Bash has a large set of logical operators that can be used in conditional expressions. The most basic form of the if control structure is to test for a condition and then execute a list of program statements if the condition is true. There are three types of operators, file, numeric and non-numeric operators. Each operator returns true (0) as its return code if the condition is met and false (1) if the condition is not met.<\/p>\n\n\n\n<p>The functional syntax of these comparison operators is one or two arguments with an operator that are placed within square braces. Then a list of program statements that are executed if the condition is true.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if &#91; arg1 operator arg2 ] ; then list<\/code><\/pre>\n\n\n\n<p>Or like this with an optional list of program statements if the condition is false.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if &#91; arg1 operator arg2 ] ; then list ; else list ; fi<\/code><\/pre>\n\n\n\n<p>The spaces in the comparison are required as shown. The single square braces, [ and ], are the traditional Bash symbols that are equivalent to the test command.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if test arg1 operator arg2 ; then list<\/code><\/pre>\n\n\n\n<p>There is also a more recent syntax that offers a few advantages and which some SysAdmins prefer. This format is a bit less compatible with different versions of Bash and other shells such as ksh (Korn shell).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if &#91;&#91; arg1 operator arg2 ]] ; then list<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">File operators<\/h3>\n\n\n\n<p>One powerful set of logical operators that are part of Bash are the file operators. Figure 1 lists more than 20 different operators that can be performed on files by Bash. I use thise quite frequently in my scripts to determine the status of various attributes belonging to a file. <\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><td><strong>Operator<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><\/thead><tbody><tr><td>-a filename<\/td><td>True if the file exists. It can be empty or have some content but so long as it exists this will be true.<\/td><\/tr><tr><td>-b filename<\/td><td>True if file exists and is a block special file such as a hard drive like \/dev\/sda or \/dev\/sda1.<\/td><\/tr><tr><td>-c filename<\/td><td>True if the file exists and is a character special file such as a TTY device like \/dev\/TTY1.<\/td><\/tr><tr><td>-d filename<\/td><td>This is true if the file exists and is a directory.<\/td><\/tr><tr><td>-e filename<\/td><td>True if file exists. This is the same as -a, above.<\/td><\/tr><tr><td>-f filename<\/td><td>True if the file exists and is a regular file as opposed to a directory a device special file or a link, among others.<\/td><\/tr><tr><td>-g filename<\/td><td>True if the file exists and is set-group-id, SETGID.<\/td><\/tr><tr><td>-h filename<\/td><td>This is true if file exists and is a symbolic link.<\/td><\/tr><tr><td>-k filename<\/td><td>True if file exists and its \u201csticky\u201d bit is set.<\/td><\/tr><tr><td>-p filename<\/td><td>True if file exists and is a named pipe (FIFO).<\/td><\/tr><tr><td>-r filename<\/td><td>True if file exists and is readable, i.e., has its read bit set.<\/td><\/tr><tr><td>-s filename<\/td><td>True if file exists and has a size greater than zero. A file that exists but that has a size of zero will return false.<\/td><\/tr><tr><td>-t fd<\/td><td>True if the file descriptor fd is open and refers to a terminal.<\/td><\/tr><tr><td>-u filename<\/td><td>True if file exists and its set-user-id bit is set.<\/td><\/tr><tr><td>-w filename<\/td><td>True if file exists and is writable.<\/td><\/tr><tr><td>-x filename<\/td><td>True if file exists and is executable.<\/td><\/tr><tr><td>-G filename<\/td><td>True if file exists and is owned by the effective group id.<\/td><\/tr><tr><td>-L filename<\/td><td>True if file exists and is a symbolic link.<\/td><\/tr><tr><td>-N filename<\/td><td>True if file exists and has been modified since it was last read.<\/td><\/tr><tr><td>-O filename<\/td><td>True if file exists and is owned by the effective user id.<\/td><\/tr><tr><td>-S filename<\/td><td>True if file exists and is a socket.<\/td><\/tr><tr><td>file1 -ef file2<\/td><td>True if file1 and file2 refer to the same device and iNode numbers.<\/td><\/tr><tr><td>file1 -nt file2<\/td><td>True if file1 is newer (according to modification date) than file2, or if file1 exists and file2 does not.<\/td><\/tr><tr><td>file1 -ot file2<\/td><td>True if file1 is older than file2, or if file2 exists and file1 does not.<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><em>Figure 1: The Bash file operators.<\/em><\/figcaption><\/figure>\n\n\n\n<p>Let\u2019s look at some examples. We will start by testing for the existence of a file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; if &#91; -e $File ] ; then echo \"The file $File exists.\" ; else echo \"The file $File does not exist.\" ; fi<\/strong>\nThe file TestFile1 does not exist.\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>Create a file for testing named TestFile1. For now it does not need to contain any data.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>touch TestFile1<\/strong><\/code><\/pre>\n\n\n\n<p>Note how easy it is to change the value of the $File variable rather than a text string for the file name in multiple locations in this short CLI program.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; if &#91; -e $File ] ; then echo \"The file $File exists.\" ; else echo \"The file $File does not exist.\" ; fi<\/strong>\nThe file TestFile1 exists.\n&#91;dboth@testvm1 testdir1]$ <\/code><\/pre>\n\n\n\n<p>Now let\u2019s determine whether a file exists and has a non-zero length which means it contains data. We have three conditions we want to test for so we need a more complex set of tests. The three conditions are; 1, the file does not exist; 2, the file exists and is empty; and 3, the file exists and contains data. To accomplish this we need to use the elif stanza in the if-elif-else construct to test for all of the conditions.<\/p>\n\n\n\n<p>Because these logic constructs can become complex I find it helpful to build them up one test at a time. In the following examples we will work up to testing for all three conditions. Let\u2019s start by testing to see if the file exists and it contains data.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; if &#91; -s $File ] ; then echo \"$File exists and contains data.\" ; fi<\/strong>\n&#91;david@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>That works but it is only truly accurate for one specific condition out of the three possible ones we have identified. Let\u2019s add an else stanza so we can be somewhat more accurate, and delete the file so that we can fully test this new code.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; rm $File ; if &#91; -s $File ] ; then echo \"$File exists and contains data.\" ; else echo \"$File does not exist or is empty.\" ; fi<\/strong>\nTestFile1 does not exist or is empty.<\/code><\/pre>\n\n\n\n<p>Finally, let\u2019s add some content to the file. and test again.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; echo \"This is file $File\" &gt; $File ; if &#91; -s $File ] ; then echo \"$File exists and contains data.\" ; else echo \"$File does not exist or is empty.\" ; fi<\/strong>\nTestFile1 exists and contains data.<\/code><\/pre>\n\n\n\n<p>Delete the file so we start this next program with a file that doesn\u2019t exist.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; rm $File<\/strong><\/code><\/pre>\n\n\n\n<p>Now we add the elif stanza to discriminate between a file that does not exist and one that is empty.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; touch $File ; if &#91; -s $File ] ; then echo \"$File exists and contains data.\" ; elif &#91; -e $File ] ; then echo \"$File exists and is empty.\" ; else echo \"$File does not exist.\" ; fi<\/strong> \nTestFile1 exists and is empty.<\/code><\/pre>\n\n\n\n<p>Finally add some data to the file.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>File=\"TestFile1\" ; echo \"This is $File\" &gt; $File ; if &#91; -s $File ] ; then echo \"$File exists and contains data.\" ; elif &#91; -e $File ] ; then echo \"$File exists and is empty.\" ; else echo \"$File does not exist.\" ; fi<\/strong>\nTestFile1 exists and contains data.\n&#91;david@testvm1 ~]$<\/code><\/pre>\n\n\n\n<p>Now we have a Bash CLI program that can test for these three different conditions and the possibilities are endless. Experiment with this some more to re-verify the code with all three conditions for the file.<\/p>\n\n\n\n<p>It is easier to see the logic structure of the more complex compound commands if we arrange the program statements more like we would in a script that we saved in a file. Figure 2 shows how this would look. The indents of the program statements in each stanza of the if-elif-else structure help to clarify the logic.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>File=\"TestFile1\"\necho \"This is $File\" &gt; $File\nif &#91; -s $File ]\nthen\n   echo \"$File exists and contains data.\"\nelif &#91; -e $File ]\nthen\n   echo \"$File exists and is empty.\"\nelse\n   echo \"$File does not exist.\"\nfi<\/code><\/pre>\n\n\n\n<p><em>Figure 2: The command line program rewritten as it would appear in a script.<\/em><\/p>\n\n\n\n<p>Logic of some complexity becomes too lengthy for most CLI programs. Although any Linux or Bash built-in commands may be used in CLI programs, as the CLI programs get longer and more complex it makes sense to create a script that is stored in a file and which can be executed at any time now or in the future. We\u2019ll get to that in the next article of this series. But for now, we have more logic operators to explore.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">String comparison operators<\/h3>\n\n\n\n<p>String comparison operators enable comparison of alphanumeric strings of characters. There are only a few of these operators which are listed in Figure 3.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><td><strong>Operator<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><\/thead><tbody><tr><td>-z string<\/td><td>True if the length of string is zero.<\/td><\/tr><tr><td>-n string<\/td><td>True if the length of string is non-zero.<\/td><\/tr><tr><td>string1 == string2 or string1 = string2<\/td><td>True if the strings are equal. A single = should be used with the test command for POSIX conformance. When used with the [[ command, this performs pattern matching as described above (Compound Commands)<\/td><\/tr><tr><td>string1 != string2<\/td><td>True if the strings are not equal.<\/td><\/tr><tr><td>string1 &lt; string2<\/td><td>True if string1 sorts before string2 lexicographically<a href=\"http:\/\/www.linux-databook.info\/?page_id=6477#sdfootnote1sym\"><sup>1<\/sup><\/a>.<\/td><\/tr><tr><td>string1 &gt; string2<\/td><td>True if string1 sorts after string2 lexicographically.<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><em>Figure 3: Bash string logical operators.<\/em><\/figcaption><\/figure>\n\n\n\n<p>First, let\u2019s look at string length. Notice that the quotes around $MyVar in the comparison itself must be there for the comparison to work. We are still working in the home directory.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>MyVar=\"\" ; if &#91; -z \"\" ] ; then echo \"MyVar is zero length.\" ; else echo \"MyVar contains data\" ; fi<\/strong>\nMyVar is zero length.\n&#91;david@testvm1 testdir1]$ <strong>MyVar=\"Random text\" ; if &#91; -z \"\" ] ; then echo \"MyVar is zero length.\" ; else echo \"MyVar contains data\" ; fi<\/strong>\nMyVar is zero length.<\/code><\/pre>\n\n\n\n<p>We could also do it this way.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>MyVar=\"Random text\" ; if &#91; -n \"$MyVar\" ] ; then echo \"MyVar contains data.\" ; else echo \"MyVar is zero length\" ; fi<\/strong>\nMyVar contains data.\n&#91;david@testvm1 testdir1]$ <strong>MyVar=\"\" ; if &#91; -n \"$MyVar\" ] ; then echo \"MyVar contains data.\" ; else echo \"MyVar is zero length\" ; fi<\/strong>\nMyVar is zero length<\/code><\/pre>\n\n\n\n<p>Since we are already talking about strings and whether they are zero length or more than zero, it might make sense that we sometimes need to know the exact length. Although this is not a comparison it is related to them. Unfortunately there is no simple way to determine the length of a string. There are a couple ways to do this but I think using the expr (evaluate expression) is the easiest. Read the man page for expr for more of what it can do. Quotes are required around the string or variable being tested.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>MyVar=\"\" ; expr length \"$MyVar\"<\/strong>\n0\n&#91;david@testvm1 testdir1]$ <strong>MyVar=\"How long is this?\" ; expr length \"$MyVar\"<\/strong>\n17\n&#91;david@testvm1 testdir1]$ <strong>expr length \"We can also find the length of a literal string as well as a variable.\"<\/strong>\n70<\/code><\/pre>\n\n\n\n<p>Back to our comparison operators. I use a lot of testing in my scripts to determine whether two strings are equal, that is, identical. I use the non-POSIX version of this comparison operator<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>Var1=\"Hello World\" ; Var2=\"Hello World\" ; if &#91; \"$Var1\" == \"$Var2\" ] ; then echo \"Var1 matches Var2\" ; else echo \"Var1 and Var2 do not match.\" ; fi<\/strong>\nVar1 matches Var2\n&#91;david@testvm1 testdir1]$ <strong>Var1=\"Hello World\" ; Var2=\"Hello world\" ; if &#91; \"$Var1\" == \"$Var2\" ] ; then echo \"Var1 matches Var2\" ; else echo \"Var1 and Var2 do not match.\" ; fi<\/strong>\nVar1 and Var2 do not match.<\/code><\/pre>\n\n\n\n<p>You can experiment some more on your own to try out these operators.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Numeric comparison operators<\/h3>\n\n\n\n<p>These numeric operators make comparisons between two numeric arguments. Like the other operator classes, most of these are easy to understand.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><td><strong>Operator<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><\/thead><tbody><tr><td>arg1 -eq arg2<\/td><td>True if arg1 equals arg2.<\/td><\/tr><tr><td>arg1 -ne arg2<\/td><td>True if arg1 is not equal to arg2.<\/td><\/tr><tr><td>arg1 -lt arg2<\/td><td>True if arg1 is less than arg2.<\/td><\/tr><tr><td>arg1 -le arg2<\/td><td>True if arg1 is less than or equal to arg2.<\/td><\/tr><tr><td>arg1 -gt arg2<\/td><td>True if arg1 is greater than arg2.<\/td><\/tr><tr><td>arg1 -ge arg2<\/td><td>True if arg1 is greater than or equal to arg2.<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><em>Figure 4: Bash numeric comparison logical operators.<\/em><\/figcaption><\/figure>\n\n\n\n<p>Here are some simple examples. In the first instance we set the variable $X to 1 and then test to see if $X is equal to 1. In the second instance, X is set to 0 so the comparison is not true.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>X=1 ; if &#91; $X -eq 1 ] ; then echo \"X equals 1\" ; else echo \"X does not equal 1\" ; fi<\/strong>\nX equals 1\n&#91;david@testvm1 testdir1]$ <strong>X=0 ; if &#91; $X -eq 1 ] ; then echo \"X equals 1\" ; else echo \"X does not equal 1\" ; fi<\/strong>\nX does not equal 1<\/code><\/pre>\n\n\n\n<p>Try some more experiments on your own.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Miscellaneous operators<\/h3>\n\n\n\n<p>These miscellaneous operators allow us to see if a shell option is set or a shell variable has a value but it does not discover the value of the variable, just whether it has one.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><td><strong>Operator<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><\/thead><tbody><tr><td>-o optname<\/td><td>True if the shell option optname is enabled. See the list of options under the description of the -o option to the Bash <strong>set<\/strong> builtin in the Bash man page.<\/td><\/tr><tr><td>-v varname<\/td><td>True if the shell variable varname is set (has been assigned a value).<\/td><\/tr><tr><td>-R varname<\/td><td>True if the shell variable varname is set and is a name reference.<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><em>Figure 5: Miscellaneous Bash logical operators.<\/em><\/figcaption><\/figure>\n\n\n\n<p>You can also experiment on your own to try out these operators.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Expansions<\/h2>\n\n\n\n<p>Bash supports a number of types of expansions and substitutions which can be quite useful. According to the Bash man page, Bash has seven (7) forms of expansions. We will look at tilde (testdir) expansion, arithmetic expansion, and pathname expansion. Unofficially I also consider the dash (-) to be the 8th form of expansion.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Brace expansion<\/h3>\n\n\n\n<p>Brace expansion is a method to use for generating arbitrary strings. Let\u2019s start with brace expansion because we will use this tool to create a large number of files to use in experiments with special pattern characters. Brace expansion can be used to generate lists of arbitrary strings and insert them into a specific location within an enclosing static string, or at either end of a static string. This may be hard to visualize so let\u2019s just do it.<\/p>\n\n\n\n<p>First let\u2019s just see what a brace expansion does.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>echo {string1,string2,string3}<\/strong>\nstring1 string2 string3<\/code><\/pre>\n\n\n\n<p>Well, that is not very helpful, is it? But look what happens when we use it just a bit differently.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>echo \"Hello \"{David,Jen,Rikki,Jason}.<\/strong>\nHello David. Hello Jen. Hello Rikki. Hello Jason.<\/code><\/pre>\n\n\n\n<p>That looks like something we might be able to use because it can save a good deal of typing. Now try this.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>echo b{ed,olt,ar}s<\/strong>\nbeds bolts bars<\/code><\/pre>\n\n\n\n<p>I could go on but you get the idea. We\u2019ll use expansions like this in Episode III<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tilde expansion<\/h3>\n\n\n\n<p>Arguably the most common expansion we run into is the tilde (~) expansion. When we use this in a command like cd ~\/Documents, the Bash shell actually expands that shortcut to the full home directory of the user. The tilde uses the value in the $PWD environment variable but the tilde requires less typing than $PWD so all of us lazy sysadmins are very happy about that. The $PWD variable is always set to the home directory of the logged-in user.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 ~]$ <strong>echo $PWD<\/strong>\n\/home\/dboth\n&#91;dboth@testvm1 ~]$<\/code><\/pre>\n\n\n\n<p>Use these little Bash programs to explore tilde expansion.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>echo ~<\/strong><br>\/home\/student<br>&#91;david@testvm1 testdir1]$ <strong>echo ~\/Documents<\/strong><br>\/home\/student\/Documents<br>&#91;david@testvm1 testdir1]$ <strong>Var1=~\/Documents ; echo $Var1 ; cd $Var1<\/strong><br>\/home\/student\/Documents<br>&#91;david@testvm1 Documents]$<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Dash expansion<\/h3>\n\n\n\n<p>The dash (-) is also a shortcut and expands to the value of the previous directory. It uses the value in the $OLDPWD environment variable. So let\u2019s first look at that value and then use the dash to return to the previous directory.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 Documents]$ <strong>echo $OLDPWD<\/strong>\n\/home\/dboth\/testdir1\n&#91;dboth@testvm1 Documents]$ <strong>cd -<\/strong>\n\/home\/dboth\/testdir1\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>The dash (-) is also a shortcut and expands to the value of the previous directory. It uses the value in the $OLDPWD environment variable. So<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pathname expansion<\/h3>\n\n\n\n<p>Pathname expansion is fancy term for expansion of file globbing patterns using the characters ? and * into the full names of directories and files that match the pattern. File globbing means special pattern characters that allow us significant flexibility in matching file names, directories, and other strings when performing various actions. These special pattern characters allow matching single, multiple or specific characters in a string.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>? \u2013 Matches only one of any character in the specified location within the string.<\/li>\n\n\n\n<li>* \u2013 Zero or more of any character in the specified location within the string.<\/li>\n<\/ul>\n\n\n\n<p>In this case we apply this expansion to matching file and directory names. Let\u2019s see how this works. Make your home directory the PWD and start with a plain listing. Of course the contents of my home directory will be different from yours.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>cd ; ls<\/strong>\nDesktop        dmesg3.txt  file0.txt  file5.txt  Music        testdir1       testfile1.txt  testfile6.txt\ndevelopment    dmesg4.txt  file1.txt  file6.txt  newfile.txt  testdir6       testfile2.txt  testfile7.txt\ndiskusage.txt  dmesg.txt   file2.txt  file7.txt  Pictures     testdir7       testfile3.txt  testfile8.txt\ndmesg1.txt     Documents   file3.txt  file8.txt  Public       testfile0.txt  testfile4.txt  testfile9.txt\ndmesg2.txt     Downloads   file4.txt  file9.txt  Templates    TestFile1      testfile5.txt  Videos\n&#91;dboth@testvm1 ~]$<\/code><\/pre>\n\n\n\n<p>Now list the directories that start with \u201cte\u201d.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 ~]$ <strong>ls te*<\/strong>\ntestfile0.txt  testfile2.txt  testfile4.txt  testfile6.txt  testfile8.txt\ntestfile1.txt  testfile3.txt  testfile5.txt  testfile7.txt  testfile9.txt\n\ntestdir1:\ndiskusage.txt  dmesg2.txt  dmesg4.txt  file0.txt  file2.txt  file4.txt  file6.txt  file8.txt  newfile.txt\ndmesg1.txt     dmesg3.txt  dmesg.txt   file1.txt  file3.txt  file5.txt  file7.txt  file9.txt  testdir2\n\ntestdir6:\n\ntestdir7:\n&#91;dboth@testvm1 ~]$<\/code><\/pre>\n\n\n\n<p>Well that did not do exactly what we want. It listed all the files and directories in the home directory as well as the contents of the directories that begin with \u201cte\u201d. To list only the current directory and its contents we can use the -d option.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 ~]$ <strong>ls -d te*<\/strong>\ntestdir1  testdir7       testfile1.txt  testfile3.txt  testfile5.txt  testfile7.txt  testfile9.txt\ntestdir6  testfile0.txt  testfile2.txt  testfile4.txt  testfile6.txt  testfile8.txt\n&#91;dboth@testvm1 ~]$<\/code><\/pre>\n\n\n\n<p>So what happens here \u2013 in both cases \u2013 is that the Bash shell expands the te* pattern into the names of the two directories and all the files that match the pattern. Any files and directories that match the pattern are expanded to their full names and displayed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Command substitution<\/h3>\n\n\n\n<p>Command substitution is a form of expansion. Command substitution is a tool that allows the STDOUT data stream of one command to be used as the argument of another command, for example, as a list of items to be processed in a loop. The Bash man page says, \u201cCommand substitution allows the output of a command to replace the command name.\u201d I find that to be accurate, if a bit obtuse.<\/p>\n\n\n\n<p>There are two forms of this substitution, `command` and $(command). In the older form using back tics (`), a backslash (\\) used in the command retains its literal meaning. However when used in the newer parenthetical form, the backslash takes on its meaning as a special character. Note also that the parenthetical form uses only single parentheses to open and close the command statement.<\/p>\n\n\n\n<p>I frequently use this capability in command line programs and scripts where the results of one command can be used as an argument for another command.<\/p>\n\n\n\n<p>Let\u2019s start with a very simple example using both forms of this expansion. Ensure that testdir1 is the PWD.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>echo \"Todays date is `date`\"<\/strong>\nTodays date is Mon Nov 20 10:41:43 AM EST 2023\n&#91;dboth@testvm1 testdir1]$ <strong>echo \"Todays date is $(date)\"<\/strong>\nTodays date is Mon Nov 20 10:41:59 AM EST 2023\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>The sequence command can be used to generate a sequence of numbers for use in Bash scripts and command line programs. This is another of those tools that I use frequently. Let\u2019s start with some simple examples.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>seq 25<\/strong>\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n&lt;SNIP&gt;\n22\n23\n24\n25\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>This stream won\u2019t sort well so the -w option to the seq utility adds leading zeros to the numbers generated to that they are all the same width, i.e., number of digits regardless of the value. This makes it easier to sort them in numeric sequence.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>seq -w 50<\/strong>\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12\n&lt;SNIP&gt;\n47\n48\n49\n50\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>Now we can do something a bit more useful like create a large number of empty files for testing.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;david@testvm1 testdir1]$ <strong>for I in $(seq -w 5000) ; do touch file-$I ; done ; ls | column | less<\/strong><\/code><\/pre>\n\n\n\n<p>In this usage, the statement <code>seq -w 5000 <\/code>generates a list of numbers from 1 to 5000. By using command substitution as part of the for statement, the list of numbers is used by the for statement to generate the numerical part of the file names. The column statement causes the output to be displayed in columns so that it can be viewed more easily by humans.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Arithmetic expansion<\/h3>\n\n\n\n<p>Bash does perform integer math but it is rather cumbersome to do so, as you will soon see. The syntax for arithmetic expansion is <code>$((arithmetic-expression)) <\/code>using double parentheses to open and close the expression. Arithmetic expansion works like command substitution in a shell program or script \u2013 the value calculated from the expression replaces the expression for further evaluation by the shell.<\/p>\n\n\n\n<p>Once again we will start with something simple.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>echo $((1+1))<\/strong>\n2\n&#91;dboth@testvm1 testdir1]$  <strong>Var1=5 ; Var2=7 ; Var3=$((Var1*Var2)) ; echo \"Var 3 = $Var3\"<\/strong>\nVar 3 = 35\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>The following division results in zero because the result would be a decimal value of less than one.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>Var1=5 ; Var2=7 ; Var3=$((Var1\/Var2)) ; echo \"Var 3 = $Var3\"<\/strong>\nVar 3 = 0\n&#91;dboth@testvm1 testdir1]$ <\/code><\/pre>\n\n\n\n<p>Here is a simple calculation that I do in a script or CLI program that tells me how much total virtual memory I have in a Linux host. The free command does not provide that data.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;dboth@testvm1 testdir1]$ <strong>RAM=`free | grep ^Mem | awk '{print $2}'` ; Swap=`free | grep ^Swap | awk '{print $2}'` ; echo \"RAM = $RAM and Swap = $Swap\" ; echo \"Total Virtual memory is $((RAM+Swap))\" ;<\/strong>\nRAM = 8117080 and Swap = 8116220\nTotal Virtual memory is 16233300\n&#91;dboth@testvm1 testdir1]$<\/code><\/pre>\n\n\n\n<p>Note that I used the ` character to delimit the sections of code that were used for command substitution. I use Bash arithmetic expansion mostly for checking system resource amounts in a script and then choosing a program execution path based on the result.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this second installment of our series on Bash as a programming language we explore the five types of Bash file, string, numeric, and miscellaneous logical operators that provide execution flow control logic; different types of shell expansions.<\/p>\n\n\n\n<p>In the third article of this series, we will explore the use of loops for performing various types of iterative operations.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Series Articles<\/h2>\n\n\n\n<p>This list contains links to all eight articles in this series about Bash.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.both.org\/?p=3030\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #1 \u2013 Introducing a New Series<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3033\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #2: Getting Started<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3038\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #3: Logical Operators<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3041\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #4: Using Loops<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3043\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #5: Automation with Scripts<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3047\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #6: Creating a template<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3052\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #7: Bash Program Needs Help<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.both.org\/?p=3055\" target=\"_blank\" rel=\"noreferrer noopener\">Programming Bash #8: Initialization and sanity testing<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>In this series of articles we are looking at Bash as a programming language. In the second article,<\/p>\n","protected":false},"author":2,"featured_media":2704,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[149,5,150],"tags":[151,152],"class_list":["post-3038","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bash","category-linux","category-programming","tag-bash","tag-programming"],"modified_by":"David Both","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/3038","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3038"}],"version-history":[{"count":10,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/3038\/revisions"}],"predecessor-version":[{"id":3778,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/3038\/revisions\/3778"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/2704"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}