{"id":1847,"date":"2018-10-28T05:47:01","date_gmt":"2018-10-28T05:47:01","guid":{"rendered":"https:\/\/www.appservgrid.com\/paw92\/?p=1847"},"modified":"2018-10-28T06:02:51","modified_gmt":"2018-10-28T06:02:51","slug":"bash-by-example","status":"publish","type":"post","link":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/28\/bash-by-example\/","title":{"rendered":"Bash by example"},"content":{"rendered":"<p>More bash programming fundamentals<\/p>\n<p>Let&#8217;s start with a brief tip on handling command-line arguments, and then<br \/>\nlook at bash&#8217;s basic programming constructs.<\/p>\n<h2>Accepting arguments<\/h2>\n<p>In the sample program in the <a href=\"http:\/\/www.ibm.com\/developerworks\/library\/l-bash.html\">introductory article<\/a>, we used the environment variable &#8220;$1&#8221;,<br \/>\nwhich referred to the first command-line argument. Similarly, you can use<br \/>\n&#8220;$2&#8221;, &#8220;$3&#8221;, etc. to refer to the second and third arguments passed to your<br \/>\nscript. Here&#8217;s an example:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>echo name of script is $0<br \/>\necho first argument is $1<br \/>\necho second argument is $2<br \/>\necho seventeenth argument is $<br \/>\necho number of arguments is $#<\/p>\n<p>The example is self explanatory except for three small details. First, &#8220;$0&#8221;<br \/>\nwill expand to the name of the script, as called from the command line,<br \/>\nsecond, for arguments 10 and above, you need to enclose the whole argument<br \/>\nnumber in curly braces, and third, &#8220;$#&#8221; will expand to the number of<br \/>\narguments passed to the script. Play around with the above script, passing<br \/>\ndifferent kinds of command-line arguments to get the hang of how it<br \/>\nworks.<\/p>\n<p>Sometimes, it&#8217;s helpful to refer to <em>all<\/em> command-line arguments at<br \/>\nonce. For this purpose, bash features the &#8220;$@&#8221; variable, which expands to<br \/>\nall command-line parameters separated by spaces. We&#8217;ll see an example of<br \/>\nits use when we take a look at &#8220;for&#8221; loops, a bit later in this<br \/>\narticle.<\/p>\n<h2>Bash programming<br \/>\nconstructs<\/h2>\n<p>If you&#8217;ve programmed in a procedural language like C, Pascal, Python, or<br \/>\nPerl, then you&#8217;re familiar with standard programming constructs like &#8220;if&#8221;<br \/>\nstatements, &#8220;for&#8221; loops, and the like. Bash has its own versions of most<br \/>\nof these standard constructs. In the next several sections, I will<br \/>\nintroduce several bash constructs and demonstrate the differences between<br \/>\nthese constructs and others you are already familiar with from other<br \/>\nprogramming languages. If you haven&#8217;t programmed much before, don&#8217;t worry.<br \/>\nI include enough information and examples so that you can follow the<br \/>\ntext.<\/p>\n<h2>Conditional love<\/h2>\n<p>If you&#8217;ve ever programmed any file-related code in C, you know that it<br \/>\nrequires a significant amount of effort to see if a particular file is<br \/>\nnewer than another. That&#8217;s because C doesn&#8217;t have any built-in syntax for<br \/>\nperforming such a comparison; instead, two stat() calls and two stat<br \/>\nstructures must be used to perform the comparison by hand. In contrast,<br \/>\nbash has standard file comparison operators built in, so determining if<br \/>\n&#8220;\/tmp\/myfile is readable&#8221; is as easy as checking to see if &#8220;$myvar is<br \/>\ngreater than 4&#8221;.<\/p>\n<p>The following table lists the most frequently used bash comparison<br \/>\noperators. You&#8217;ll also find an example of how to use every option<br \/>\ncorrectly. The example is meant to be placed immediately after the &#8220;if&#8221;.<br \/>\nFor example:<\/p>\n<p>if [ -z &#8220;$myvar&#8221; ]<br \/>\nthen<br \/>\necho &#8220;myvar is not defined&#8221;<br \/>\nfi<\/p>\n<p>Note: You must separate the square brackets from other<br \/>\ntext by a space.<\/p>\n<h5>Common Bash comparisons<\/h5>\n<table>\n<thead>\n<tr>\n<th>Operator<\/th>\n<th>Meaning<\/th>\n<th>Example<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>-z<\/td>\n<td>Zero-length string<\/td>\n<td>[ -z &#8220;$myvar&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-n<\/td>\n<td>Non-zero-length string<\/td>\n<td>[ -n &#8220;$myvar&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>=<\/td>\n<td>String equality<\/td>\n<td>[ &#8220;abc&#8221; = &#8220;$myvar&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>!=<\/td>\n<td>String inequality<\/td>\n<td>[ &#8220;abc&#8221; != &#8220;$myvar&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-eq<\/td>\n<td>Numeric equality<\/td>\n<td>[ 3 -eq &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-ne<\/td>\n<td>Numeric inequality<\/td>\n<td>[ 3 -ne &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-lt<\/td>\n<td>Numeric strict less than<\/td>\n<td>[ 3 -lt &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-le<\/td>\n<td>Numeric less than or equals<\/td>\n<td>[ 3 -le &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-gt<\/td>\n<td>Numeric strict greater than<\/td>\n<td>[ 3 -gt &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-ge<\/td>\n<td>Numeric greater than or equals<\/td>\n<td>[ 3 -ge &#8220;$myinteger&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-f<\/td>\n<td>Exists and is regular file<\/td>\n<td>[ -f &#8220;$myfile&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-d<\/td>\n<td>Exists and is directory<\/td>\n<td>[ -d &#8220;$mydir&#8221; ]<\/td>\n<\/tr>\n<tr>\n<td>-nt<\/td>\n<td>First file is newer than second one<\/td>\n<td>[ &#8220;$myfile&#8221; -nt ~\/.bashrc ]<\/td>\n<\/tr>\n<tr>\n<td>-ot<\/td>\n<td>First file is older than second one<\/td>\n<td>[ &#8220;$myfile&#8221; -ot ~\/.bashrc ]<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Sometimes, there are several different ways that a particular comparison<br \/>\ncan be made. For example, the following two snippets of code function<br \/>\nidentically:<\/p>\n<p>if [ &#8220;$myvar&#8221; -eq 3 ]<br \/>\nthen<br \/>\necho &#8220;myvar equals 3&#8221;<br \/>\nfi<\/p>\n<p>if [ &#8220;$myvar&#8221; = &#8220;3&#8221; ]<br \/>\nthen<br \/>\necho &#8220;myvar equals 3&#8221;<br \/>\nfi<\/p>\n<p>If $myvar is an integer, these two comparisons do<br \/>\nexactly the same thing, but the first uses arithmetic comparison<br \/>\noperators, while the second uses string comparison operators.<\/p>\n<p>If $myvar is not an integer,<\/p>\n<p>then the first comparison will fail with an error.<\/p>\n<h2>String comparison caveats<\/h2>\n<p>Most of the time, while you can omit the use of double quotes surrounding<br \/>\nstrings and string variables, it&#8217;s not a good idea. Why? Because your code<br \/>\nwill work perfectly, unless an environment variable happens to have a<br \/>\nspace or a tab in it, in which case bash will get confused. Here&#8217;s an<br \/>\nexample of a fouled-up comparison:<\/p>\n<p>if [ $myvar = &#8220;foo bar oni&#8221; ]<br \/>\nthen<br \/>\necho &#8220;yes&#8221;<br \/>\nfi<\/p>\n<p>In the above example, if myvar equals &#8220;foo&#8221;, the code will work as expected<br \/>\nand not print anything. However, if myvar equals &#8220;foo bar oni&#8221;, the code<br \/>\nwill fail with the following error:<\/p>\n<p>[: too many arguments<\/p>\n<p>In this case, the spaces in &#8220;$myvar&#8221; (which equals &#8220;foo bar oni&#8221;) end up<br \/>\nconfusing bash. After bash expands &#8220;$myvar&#8221;, it ends up with the following<br \/>\ncomparison:<\/p>\n<p>[ foo bar oni = &#8220;foo bar oni&#8221; ]<\/p>\n<p>Similarly, if myvar is the empty string, you will have too few arguments<br \/>\nand the code will fail with the following error:<\/p>\n<p>[: =: unary operator expected<\/p>\n<p>Because the environment variable wasn&#8217;t placed inside double quotes, bash<br \/>\nthinks that you stuffed too many (or too few) arguments in-between the<br \/>\nsquare brackets. You can easily eliminate this problem by surrounding the<br \/>\nstring arguments with double-quotes. Remember, if you get into the habit<br \/>\nof surrounding all string arguments and environment variables with<br \/>\ndouble-quotes, you&#8217;ll eliminate many similar programming errors. Here&#8217;s<br \/>\nhow the &#8220;foo bar oni&#8221; comparison <em>should<\/em> have been written:<\/p>\n<p>if [ &#8220;$myvar&#8221; = &#8220;foo bar oni&#8221; ]<br \/>\nthen<br \/>\necho &#8220;yes&#8221;<br \/>\nfi<\/p>\n<h5>More quoting specifics<\/h5>\n<p>If you want your environment variables to be expanded, you must enclose them<br \/>\nin <em>double quotes<\/em>, rather than single quotes. Single quotes<br \/>\n<em>disable<\/em> variable (as well as history) expansion.<\/p>\n<p>The above code will work as expected and will not create any unpleasant surprises.<\/p>\n<h2>Looping constructs: &#8220;for&#8221;<\/h2>\n<p>OK, we&#8217;ve covered conditionals, now it&#8217;s time to explore bash looping<br \/>\nconstructs. We&#8217;ll start with the standard &#8220;for&#8221; loop. Here&#8217;s a basic<br \/>\nexample:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>for x in one two three four<br \/>\ndo<br \/>\necho number $x<br \/>\ndone<\/p>\n<p>output:<br \/>\nnumber one<br \/>\nnumber two<br \/>\nnumber three<br \/>\nnumber four<\/p>\n<p>What exactly happened? The &#8220;for x&#8221; part of our &#8220;for&#8221; loop defined a new<br \/>\nenvironment variable (also called a loop control variable) called &#8220;$x&#8221;,<br \/>\nwhich was successively set to the values &#8220;one&#8221;, &#8220;two&#8221;, &#8220;three&#8221;, and<br \/>\n&#8220;four&#8221;. After each assignment, the body of the loop (the code between the<br \/>\n&#8220;do&#8221; &#8230; &#8220;done&#8221;) was executed once. In the body, we referred to the loop<br \/>\ncontrol variable &#8220;$x&#8221; using standard variable expansion syntax, like any<br \/>\nother environment variable. Also notice that &#8220;for&#8221; loops always accept<br \/>\nsome kind of word list after the &#8220;in&#8221; statement. In this case we specified<br \/>\nfour English words, but the word list can also refer to file(s) on disk or<br \/>\neven file wildcards. Look at the following example, which demonstrates how<br \/>\nto use standard shell wildcards:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>for myfile in \/etc\/r*<br \/>\ndo<br \/>\nif [ -d &#8220;$myfile&#8221; ]<br \/>\nthen<br \/>\necho &#8220;$myfile (dir)&#8221;<br \/>\nelse<br \/>\necho &#8220;$myfile&#8221;<br \/>\nfi<br \/>\ndone<\/p>\n<p>output:<\/p>\n<p>\/etc\/rc.d (dir)<br \/>\n\/etc\/resolv.conf<br \/>\n\/etc\/resolv.conf~<br \/>\n\/etc\/rpc<\/p>\n<p>The above code looped over each file in \/etc that began with an &#8220;r&#8221;. To do<br \/>\nthis, bash first took our wildcard \/etc\/r* and expanded it, replacing it<br \/>\nwith the string \/etc\/rc.d \/etc\/resolv.conf \/etc\/resolv.conf~ \/etc\/rpc<br \/>\nbefore executing the loop. Once inside the loop, the &#8220;-d&#8221; conditional<br \/>\noperator was used to perform two different actions, depending on whether<br \/>\nmyfile was a directory or not. If it was, a &#8221; (dir)&#8221; was appended to the<br \/>\noutput line.<\/p>\n<p>We can also use multiple wildcards and even environment variables in the<br \/>\nword list:<\/p>\n<p>for x in \/etc\/r??? \/var\/lo* \/home\/drobbins\/mystuff\/* \/tmp\/$\/*<br \/>\ndo<br \/>\ncp $x \/mnt\/mydir<br \/>\ndone<\/p>\n<p>Bash will perform wildcard and variable expansion in all the right places,<br \/>\nand potentially create a very long word list.<\/p>\n<p>While all of our wildcard expansion examples have used <em>absolute<\/em><br \/>\npaths, you can also use relative paths, as follows:<\/p>\n<p>for x in ..\/* mystuff\/*<br \/>\ndo<br \/>\necho $x is a silly file<br \/>\ndone<\/p>\n<p>In the above example, bash performs wildcard expansion relative to the<br \/>\ncurrent working directory, just like when you use relative paths on the<br \/>\ncommand line. Play around with wildcard expansion a bit. You&#8217;ll notice<br \/>\nthat if you use absolute paths in your wildcard, bash will expand the<br \/>\nwildcard to a list of absolute paths. Otherwise, bash will use relative<br \/>\npaths in the subsequent word list. If you simply refer to files in the<br \/>\ncurrent working directory (for example, if you type &#8220;for x in *&#8221;), the<br \/>\nresultant list of files will not be prefixed with any path information.<br \/>\nRemember that preceding path information can be stripped using the<br \/>\n&#8220;basename&#8221; executable, as follows:<\/p>\n<p>for x in \/var\/log\/*<br \/>\ndo<br \/>\necho `basename $x` is a file living in \/var\/log<br \/>\ndone<\/p>\n<p>Of course, it&#8217;s often handy to perform loops that operate on a script&#8217;s<br \/>\ncommand-line arguments. Here&#8217;s an example of how to use the &#8220;$@&#8221; variable,<br \/>\nintroduced at the beginning of this article:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>for thing in &#8220;$@&#8221;<br \/>\ndo<br \/>\necho you typed $.<br \/>\ndone<\/p>\n<p>output:<\/p>\n<p>$ allargs hello there you silly<br \/>\nyou typed hello.<br \/>\nyou typed there.<br \/>\nyou typed you.<br \/>\nyou typed silly.<\/p>\n<h2>Shell arithmetic<\/h2>\n<p>Before looking at a second type of looping construct, it&#8217;s a good idea to<br \/>\nbecome familiar with performing shell arithmetic. Yes, it&#8217;s true: You can<br \/>\nperform simple integer math using shell constructs. Simply enclose the<br \/>\nparticular arithmetic expression between a &#8220;$((&#8221; and a &#8220;))&#8221;, and bash will<br \/>\nevaluate the expression. Here are some examples:<\/p>\n<p>$ echo $(( 100 \/ 3 ))<br \/>\n33<br \/>\n$ myvar=&#8221;56&#8243;<br \/>\n$ echo $(( $myvar + 12 ))<br \/>\n68<br \/>\n$ echo $(( $myvar &#8211; $myvar ))<br \/>\n0<br \/>\n$ myvar=$(( $myvar + 1 ))<br \/>\n$ echo $myvar<br \/>\n57<\/p>\n<p>Now that you&#8217;re familiar performing mathematical operations, it&#8217;s time to<br \/>\nintroduce two other bash looping constructs, &#8220;while&#8221; and &#8220;until&#8221;.<\/p>\n<h2>More looping constructs: &#8220;while&#8221;<br \/>\nand &#8220;until&#8221;<\/h2>\n<p>A &#8220;while&#8221; statement will execute as long as a particular condition is<br \/>\ntrue, and has the following format:<\/p>\n<p>while [ condition ]<br \/>\ndo<br \/>\nstatements<br \/>\ndone<\/p>\n<p>&#8220;While&#8221; statements are typically used to loop a certain number of times, as<br \/>\nin the following example, which will loop exactly 10 times:<\/p>\n<p>myvar=0<br \/>\nwhile [ $myvar -ne 10 ]<br \/>\ndo<br \/>\necho $myvar<br \/>\nmyvar=$(( $myvar + 1 ))<br \/>\ndone<\/p>\n<p>You can see the use of arithmetic expansion to eventually cause the<br \/>\ncondition to be false, and the loop to terminate.<\/p>\n<p>&#8220;Until&#8221; statements provide the inverse functionality of &#8220;while&#8221; statements:<br \/>\nThey repeat as long as a particular condition is <em>false<\/em>. Here&#8217;s an<br \/>\n&#8220;until&#8221; loop that functions identically to the previous &#8220;while&#8221; loop:<\/p>\n<p>myvar=0<br \/>\nuntil [ $myvar -eq 10 ]<br \/>\ndo<br \/>\necho $myvar<br \/>\nmyvar=$(( $myvar + 1 ))<br \/>\ndone<\/p>\n<h2>Case statements<\/h2>\n<p>Case statements are another conditional construct that comes in handy.<br \/>\nHere&#8217;s an example snippet:<\/p>\n<p>case &#8220;$&#8221; in<br \/>\ngz)<br \/>\ngzunpack $\/$<br \/>\n;;<br \/>\nbz2)<br \/>\nbz2unpack $\/$<br \/>\n;;<br \/>\n*)<br \/>\necho &#8220;Archive format not recognized.&#8221;<br \/>\nexit<br \/>\n;;<br \/>\nesac<\/p>\n<p>Above, bash first expands &#8220;$&#8221;. In the code, &#8220;$x&#8221; is the name of a<br \/>\nfile, and &#8220;$&#8221; has the effect of stripping all text except that<br \/>\nfollowing the last period in the filename. Then, bash compares the<br \/>\nresultant string against the values listed to the left of the &#8220;)&#8221;s. In<br \/>\nthis case, &#8220;$&#8221; gets compared against &#8220;gz&#8221;, then &#8220;bz2&#8221; and finally<br \/>\n&#8220;*&#8221;. If &#8220;$&#8221; matches any of these strings or patterns, the lines<br \/>\nimmediately following the &#8220;)&#8221; are executed, up until the &#8220;;;&#8221;, at which<br \/>\npoint bash continues executing lines after the terminating &#8220;esac&#8221;. If no<br \/>\npatterns or strings are matched, no lines of code are executed; however,<br \/>\nin this particular code snippet, at least one block of code will execute,<br \/>\nbecause the &#8220;*&#8221; pattern will catch everything that didn&#8217;t match &#8220;gz&#8221; or<br \/>\n&#8220;bz2&#8221;.<\/p>\n<h2>Functions and namespaces<\/h2>\n<p>In bash, you can even define functions, similar to those in other<br \/>\nprocedural languages like Pascal and C. In bash, functions can even accept<br \/>\narguments, using a system very similar to the way scripts accept<br \/>\ncommand-line arguments. Let&#8217;s take a look at a sample function definition<br \/>\nand then proceed from there:<\/p>\n<p>tarview() {<br \/>\necho -n &#8220;Displaying contents of $1 &#8221;<br \/>\nif [ $ = tar ]<br \/>\nthen<br \/>\necho &#8220;(uncompressed tar)&#8221;<br \/>\ntar tvf $1<br \/>\nelif [ $ = gz ]<br \/>\nthen<br \/>\necho &#8220;(gzip-compressed tar)&#8221;<br \/>\ntar tzvf $1<br \/>\nelif [ $ = bz2 ]<br \/>\nthen<br \/>\necho &#8220;(bzip2-compressed tar)&#8221;<br \/>\ncat $1 | bzip2 -d | tar tvf &#8211;<br \/>\nfi<br \/>\n}<\/p>\n<h5>Another case<\/h5>\n<p>The above code could have been written using a &#8220;case&#8221; statement.<br \/>\nCan you figure out how?<\/p>\n<p>Above, we define a function called &#8220;tarview&#8221; that accepts one argument, a<br \/>\ntarball of some kind. When the function is executed, it identifies what<br \/>\ntype of tarball the argument is (either uncompressed, gzip-compressed, or<br \/>\nbzip2-compressed), prints out a one-line informative message, and then<br \/>\ndisplays the contents of the tarball. This is how the above function<br \/>\nshould be called (whether from a script or from the command line, after it<br \/>\nhas been typed in, pasted in, or sourced):<\/p>\n<p>$ tarview shorten.tar.gz<br \/>\nDisplaying contents of shorten.tar.gz (gzip-compressed tar)<br \/>\ndrwxr-xr-x ajr\/abbot 0 1999-02-27 16:17 shorten-2.3a\/<br \/>\n-rw-r&#8211;r&#8211; ajr\/abbot 1143 1997-09-04 04:06 shorten-2.3a\/Makefile<br \/>\n-rw-r&#8211;r&#8211; ajr\/abbot 1199 1996-02-04 12:24 shorten-2.3a\/INSTALL<br \/>\n-rw-r&#8211;r&#8211; ajr\/abbot 839 1996-05-29 00:19 shorten-2.3a\/LICENSE<br \/>\n&#8230;.<\/p>\n<h5>Use &#8217;em interactively<\/h5>\n<p>Don&#8217;t forget that functions, like the one above, can be placed<br \/>\nin your ~\/.bashrc or ~\/.bash_profile so that they are available<br \/>\nfor use whenever you are in bash.<\/p>\n<p>As you can see, arguments can be referenced inside the function definition<br \/>\nby using the same mechanism used to reference command-line arguments. In<br \/>\naddition, the &#8220;$#&#8221; macro will be expanded to contain the number of<br \/>\narguments. The only thing that may not work completely as expected is the<br \/>\nvariable &#8220;$0&#8221;, which will either expand to the string &#8220;bash&#8221; (if you run<br \/>\nthe function from the shell, interactively) or to the name of the script<br \/>\nthe function is called from.<\/p>\n<h2>Namespace<\/h2>\n<p>Often, you&#8217;ll need to create environment variables inside a function.<br \/>\nWhile possible, there&#8217;s a technicality you should know about. In most<br \/>\ncompiled languages (such as C), when you create a variable inside a<br \/>\nfunction, it&#8217;s placed in a separate local namespace. So, if you define a<br \/>\nfunction in C called myfunction, and in it define a variable called &#8220;x&#8221;,<br \/>\nany global (outside the function) variable called &#8220;x&#8221; will not be affected<br \/>\nby it, eliminating side effects.<\/p>\n<p>While true in C, this isn&#8217;t true in bash. In bash, whenever you create an<br \/>\nenvironment variable inside a function, it&#8217;s added to the <em>global<\/em><br \/>\nnamespace. This means that it will overwrite any global variable outside<br \/>\nthe function, and will continue to exist even after the function<br \/>\nexits:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>myvar=&#8221;hello&#8221;<\/p>\n<p>myfunc() {<\/p>\n<p>myvar=&#8221;one two three&#8221;<br \/>\nfor x in $myvar<br \/>\ndo<br \/>\necho $x<br \/>\ndone<br \/>\n}<\/p>\n<p>myfunc<\/p>\n<p>echo $myvar $x<\/p>\n<p>When this script is run, it produces the output &#8220;one two three three&#8221;,<br \/>\nshowing how &#8220;$myvar&#8221; defined in the function clobbered the global variable<br \/>\n&#8220;$myvar&#8221;, and how the loop control variable &#8220;$x&#8221; continued to exist even<br \/>\nafter the function exited (and also would have clobbered any global &#8220;$x&#8221;,<br \/>\nif one were defined).<\/p>\n<p>In this simple example, the bug is easy to spot and to compensate for by<br \/>\nusing alternate variable names. However, this isn&#8217;t the right approach;<br \/>\nthe best way to solve this problem is to prevent the possibility of<br \/>\nclobbering global variables in the first place, by using the &#8220;local&#8221;<br \/>\ncommand. When we use &#8220;local&#8221; to create variables inside a function, they<br \/>\nwill be kept in the <em>local<\/em> namespace and not clobber any global<br \/>\nvariables. Here&#8217;s how to implement the above code so that no global<br \/>\nvariables are overwritten:<\/p>\n<p>#!\/usr\/bin\/env bash<\/p>\n<p>myvar=&#8221;hello&#8221;<\/p>\n<p>myfunc() {<br \/>\nlocal x<br \/>\nlocal myvar=&#8221;one two three&#8221;<br \/>\nfor x in $myvar<br \/>\ndo<br \/>\necho $x<br \/>\ndone<br \/>\n}<\/p>\n<p>myfunc<\/p>\n<p>echo $myvar $x<\/p>\n<p>This function will produce the output &#8220;hello&#8221; &#8212; the global &#8220;$myvar&#8221;<br \/>\ndoesn&#8217;t get overwritten, and &#8220;$x&#8221; doesn&#8217;t continue to exist outside of<br \/>\nmyfunc. In the first line of the function, we create x, a local variable<br \/>\nthat is used later, while in the second example (local myvar=&#8221;one two<br \/>\nthree&#8221;&#8221;) we create a local myvar <em>and<\/em> assign it a value. The first<br \/>\nform is handy for keeping loop control variables local, since we&#8217;re not<br \/>\nallowed to say &#8220;for local x in $myvar&#8221;. This function doesn&#8217;t clobber any<br \/>\nglobal variables, and you are encouraged to design all your functions this<br \/>\nway. The only time you should <em>not<\/em> use &#8220;local&#8221; is when you<br \/>\nexplicitly want to modify a global variable.<\/p>\n<h2>Wrapping it up<\/h2>\n<p>Now that we&#8217;ve covered the most essential bash functionality, it&#8217;s time to<br \/>\nlook at how to develop an entire application based in bash. In my next<br \/>\ninstallment, we&#8217;ll do just that. See you then!<\/p>\n<h4>Downloadable resources<\/h4>\n<ul>\n<li><a href=\"l-bash2-pdf.pdf\">PDF of this content<\/a><\/li>\n<\/ul>\n<ul>\n<li>Read <a href=\"http:\/\/www.ibm.com\/developerworks\/library\/l-bash.html\">&#8220;Bash by example: Part 1&#8221;<\/a> on <em>developerWorks<\/em>.<\/li>\n<li>Read <a href=\"http:\/\/www.ibm.com\/developerworks\/library\/l-bash3.html\">&#8220;Bash by example: Part 3&#8221;<\/a> on <em>developerWorks<\/em>.<\/li>\n<li>Visit <a href=\"http:\/\/www.gnu.org\/software\/bash\/bash.html\">GNU&#8217;s bash<br \/>\nhome page<\/a>.<\/li>\n<\/ul>\n<p>Subscribe me to comment notifications<\/p>\n<p><a href=\"http:\/\/lxer.com\/module\/newswire\/ext_link.php?rid=261993\" target=\"_blank\" rel=\"noopener\">Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>More bash programming fundamentals Let&#8217;s start with a brief tip on handling command-line arguments, and then look at bash&#8217;s basic programming constructs. Accepting arguments In the sample program in the introductory article, we used the environment variable &#8220;$1&#8221;, which referred to the first command-line argument. Similarly, you can use &#8220;$2&#8221;, &#8220;$3&#8221;, etc. to refer to &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/28\/bash-by-example\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Bash by example&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1847","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/1847","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/comments?post=1847"}],"version-history":[{"count":1,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/1847\/revisions"}],"predecessor-version":[{"id":1878,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/1847\/revisions\/1878"}],"wp:attachment":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/media?parent=1847"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/categories?post=1847"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/tags?post=1847"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}