Files
hpr_website/www/eps/hpr2448/hpr2448_full_shownotes.html

458 lines
28 KiB
HTML
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="generator" content="pandoc">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<meta name="author" content="Dave Morriss">
<title>Useful Bash functions - part 3 (HPR Show 2448)</title>
<style type="text/css">code{white-space: pre;}</style>
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style type="text/css">
div.sourceCode { overflow-x: auto; }
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
margin: 0; padding: 0; vertical-align: baseline; border: none; }
table.sourceCode { width: 100%; line-height: 100%; }
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
code > span.dt { color: #902000; } /* DataType */
code > span.dv { color: #40a070; } /* DecVal */
code > span.bn { color: #40a070; } /* BaseN */
code > span.fl { color: #40a070; } /* Float */
code > span.ch { color: #4070a0; } /* Char */
code > span.st { color: #4070a0; } /* String */
code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
code > span.ot { color: #007020; } /* Other */
code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
code > span.fu { color: #06287e; } /* Function */
code > span.er { color: #ff0000; font-weight: bold; } /* Error */
code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
code > span.cn { color: #880000; } /* Constant */
code > span.sc { color: #4070a0; } /* SpecialChar */
code > span.vs { color: #4070a0; } /* VerbatimString */
code > span.ss { color: #bb6688; } /* SpecialString */
code > span.im { } /* Import */
code > span.va { color: #19177c; } /* Variable */
code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code > span.op { color: #666666; } /* Operator */
code > span.bu { } /* BuiltIn */
code > span.ex { } /* Extension */
code > span.pp { color: #bc7a00; } /* Preprocessor */
code > span.at { color: #7d9029; } /* Attribute */
code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
</style>
<link rel="stylesheet" href="http://hackerpublicradio.org/css/hpr.css">
</head>
<body id="home">
<div id="container" class="shadow">
<header>
<h1 class="title">Useful Bash functions - part 3 (HPR Show 2448)</h1>
<h2 class="author">Dave Morriss</h2>
<hr/>
</header>
<main id="maincontent">
<article>
<header>
<h1>Table of Contents</h1>
<nav id="TOC">
<ul>
<li><a href="#overview">Overview</a></li>
<li><a href="#example-functions">Example Functions</a><ul>
<li><a href="#the-read_value-function">The <strong><code>read_value</code></strong> function</a></li>
<li><a href="#the-check_value-function">The <strong><code>check_value</code></strong> function</a></li>
<li><a href="#the-read_and_check-function">The <strong><code>read_and_check</code></strong> function</a></li>
</ul></li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#links">Links</a></li>
</ul>
</nav>
</header>
<h2 id="overview">Overview</h2>
<p>This is the third show about Bash functions. These are a little more advanced than in the earlier shows, and I thought Id share them in case they are useful to anyone.</p>
<p>As before it would be interesting to receive feedback on these functions and would be great if other Bash users contributed ideas of their own.</p>
<h2 id="example-functions">Example Functions</h2>
<h3 id="the-read_value-function">The <strong><code>read_value</code></strong> function</h3>
<p>The purpose of this function is to output a prompt and read a string. The string is written to a nominated variable and a default string can be provided if required.</p>
<p>A typical call might be:</p>
<pre><code>$ read_value &#39;What is your name? &#39; name
What is your name? Herbert
$ echo $name
Herbert</code></pre>
<p>Here, the first argument is the prompt and the second is the name of the variable to receive the answer.</p>
<p>If the default is used then the reply is pre-filled with it:</p>
<pre><code>$ read_value &#39;Where do you live? &#39; country USA
Where do you live? USA</code></pre>
<p>This prompt can be deleted and another value used instead.</p>
<div class="sourceCode"><table class="sourceCode bash numberLines"><tr class="sourceCode"><td class="lineNumbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
</pre></td><td class="sourceCode"><pre><code class="sourceCode bash"><span class="co">#=== FUNCTION ================================================================</span>
<span class="co"># NAME: read_value</span>
<span class="co"># DESCRIPTION: Read a value from STDIN and handle errors.</span>
<span class="co"># PARAMETERS: 1 - Prompt string for the read</span>
<span class="co"># 2 - Name of variable to receive the result</span>
<span class="co"># 3 - Default value (optional)</span>
<span class="co"># RETURNS: 1 on error, otherwise 0</span>
<span class="co">#===============================================================================</span>
<span class="fu">read_value ()</span> <span class="kw">{</span>
<span class="bu">local</span> <span class="va">prompt=</span><span class="st">&quot;</span><span class="va">${1:?</span>Usage: read_value prompt outputname [default]<span class="va">}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">outputname=</span><span class="st">&quot;</span><span class="va">${2:?</span>Usage: read_value prompt outputname [default]<span class="va">}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">default=</span><span class="st">&quot;</span><span class="va">${3:-}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">var</span>
<span class="co">#</span>
<span class="co"># Make an option for the &#39;read&#39; if there&#39;s a default</span>
<span class="co">#</span>
<span class="kw">if [[</span> <span class="ot">-n</span> <span class="va">$default</span><span class="kw"> ]]</span>; <span class="kw">then</span>
<span class="va">default=</span><span class="st">&quot;-i &#39;</span><span class="va">$default</span><span class="st">&#39;&quot;</span>
<span class="kw">fi</span>
<span class="co">#</span>
<span class="co"># Read and handle CTRL-D (EOF). Use &#39;eval&#39; to deal with the argument being</span>
<span class="co"># a variable</span>
<span class="co">#</span>
<span class="bu">eval</span> <span class="st">&quot;read -r -e </span><span class="va">$default</span><span class="st"> -p &#39;</span><span class="va">$prompt</span><span class="st">&#39; var&quot;</span>
<span class="va">res=</span><span class="st">&quot;</span><span class="va">$?</span><span class="st">&quot;</span>
<span class="kw">if [[</span> <span class="va">$res</span> <span class="ot">-ne</span> 0<span class="kw"> ]]</span>; <span class="kw">then</span>
<span class="bu">echo</span> <span class="st">&quot;Read aborted&quot;</span>
<span class="bu">return</span> 1
<span class="kw">fi</span>
<span class="co">#</span>
<span class="co"># Return the value in the nominated variable</span>
<span class="co">#</span>
<span class="bu">eval</span> <span class="st">&quot;</span><span class="va">$outputname</span><span class="st">=&#39;</span><span class="va">$var</span><span class="st">&#39;&quot;</span>
<span class="bu">return</span> 0
<span class="kw">}</span></code></pre></td></tr></table></div>
<p>The function is not very complex and has a number of similarities with the various iterations of the <code>yes_no</code> function encountered in earlier episodes. See the <a href="#links">Links</a> section below for links to these episodes.</p>
<p>We will not dwell too long on this function as a consequence of these similarities. However, it does something the previous functions didnt do, it returns a string to the caller.</p>
<p>Bash functions cant return anything but numeric values (via <code>return</code>), unlike higher level languages. This function writes a global variable with the value it has requested. It could have been designed to always write to the same global variable, but this is ugly. I wanted the caller to be able to nominate the variable to receive the result.</p>
<p>The main purpose of the script is to call the Bash built-in command <code>read</code> with various options. If there is a default string provided we need to turn that into an option preceded by <code>-i</code>. This is achieved on lines 18-20 where we store the result back in the variable <code>default</code>.</p>
<p>The <code>read</code> is performed on line 26, and we use the <code>eval</code> command to do this. This used because we need to make Bash scan the line twice, and the way <code>eval</code> works allows us to achieve this.</p>
<p>The <code>eval</code> command takes its arguments (in this case a string), concatenates them and executes the result in the current environment. The string it is presented with in this case is first processed by Bash where it performs the various types of expansion.</p>
<p>If we look at an example this might help to clarify the issue (the function has to be made available to Bash using the <code>source</code> command before this will work):</p>
<pre><code>$ read_value &#39;What is your surname? &#39; surname &#39;Not provided&#39;</code></pre>
<p>Here, the variable <code>default</code> will contain Not provided (without the quotes, which get stripped when the command is parsed by Bash), and that will be converted to “<code>-i 'Not provided'</code>”. The argument to <code>eval</code> will then be expanded to:</p>
<pre><code>read -r -e -i &#39;Not provided&#39; -p &#39;What is your surname? &#39; var</code></pre>
<p>This command will then be executed.</p>
<p>If we had not used <code>eval</code> and had instead written:</p>
<pre><code>read -r -e $default -p &quot;$prompt&quot; var</code></pre>
<p>The contents of <code>default</code> would not have been parsed by Bash. Using <code>eval</code> causes two scans of the command. First time the parameters are substituted, and the second time the command is executed.</p>
<p>The following is the result of running the function on the command line with tracing on (<code>set -x</code>):</p>
<pre><code>$ set -x
$ read_value &#39;What is your surname? &#39; surname &#39;Not provided&#39;
+ read_value &#39;What is your surname? &#39; surname &#39;Not provided&#39;
+ local &#39;prompt=What is your surname? &#39;
+ local outputname=surname
+ local &#39;default=Not provided&#39;
+ local var
+ [[ -n Not provided ]]
+ default=&#39;-i &#39;\&#39;&#39;Not provided&#39;\&#39;&#39;&#39;
+ eval &#39;read -r -e -i &#39;\&#39;&#39;Not provided&#39;\&#39;&#39; -p &#39;\&#39;&#39;What is your surname? &#39;\&#39;&#39; var&#39;
++ read -r -e -i &#39;Not provided&#39; -p &#39;What is your surname? &#39; var
What is your surname? Putin
+ res=0
+ [[ 0 -ne 0 ]]
+ eval &#39;surname=&#39;\&#39;&#39;Putin&#39;\&#39;&#39;&#39;
++ surname=Putin
+ return 0
$ set +x; echo &quot;$surname&quot;
+ set +x
Putin
</code></pre>
<p>The lines that start with a <code>$</code> are commands I typed, and those beginning with a <code>+</code> are the output of Bashs trace mode. I added blank lines after each command (plus any output it generated).</p>
<ul>
<li>You can see the function arguments being placed in local variables.</li>
<li>The <code>default</code> value is saved</li>
<li>When the <code>eval</code> is shown all of the variables used are expanded, then the resulting command is run</li>
<li>The prompt is shown, with my response “Putin”</li>
<li>The <code>eval</code> that returns the result in variable <code>surname</code> is shown (the variable name is in the local variable <code>var</code>)</li>
<li>I turn off the trace (<code>set +x</code>) and echo the result in this variable.</li>
</ul>
<h3 id="the-check_value-function">The <strong><code>check_value</code></strong> function</h3>
<p>This function was designed to be used in conjunction with <code>read_value</code> to check that the string read in is valid.</p>
<div class="sourceCode"><table class="sourceCode bash numberLines"><tr class="sourceCode"><td class="lineNumbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
</pre></td><td class="sourceCode"><pre><code class="sourceCode bash"><span class="co">#=== FUNCTION ================================================================</span>
<span class="co"># NAME: check_value</span>
<span class="co"># DESCRIPTION: Checks a value against a list of regular expressions</span>
<span class="co"># PARAMETERS: 1 - the value to be checked</span>
<span class="co"># 2..n - valid Bash regular expressions</span>
<span class="co"># RETURNS: 0 if the value checks, otherwise 1</span>
<span class="co">#===============================================================================</span>
<span class="fu">check_value ()</span> <span class="kw">{</span>
<span class="bu">local</span> <span class="va">value=</span><span class="st">&quot;</span><span class="va">${1?</span>Usage: check_value value list_of_regex<span class="va">}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">matches=</span>0
<span class="co">#</span>
<span class="co"># Drop parameter 1 then there should be more</span>
<span class="co">#</span>
<span class="bu">shift</span>
<span class="kw">if [[</span> <span class="va">$#</span> <span class="ot">==</span> 0<span class="kw"> ]]</span>; <span class="kw">then</span>
<span class="bu">echo</span> <span class="st">&quot;Usage: check_value value list_of_regex&quot;</span>
<span class="bu">return</span> 1
<span class="kw">fi</span>
<span class="co">#</span>
<span class="co"># Loop through the regex args checking the value, counting matches</span>
<span class="co">#</span>
<span class="kw">while [[</span> <span class="va">$#</span> <span class="ot">-ge</span> 1<span class="kw"> ]]</span>
<span class="kw">do</span>
<span class="kw">if [[</span> <span class="va">$value</span> =~ <span class="va">$1</span><span class="kw"> ]]</span>; <span class="kw">then</span>
<span class="kw">((</span> matches++ <span class="kw">))</span>
<span class="kw">fi</span>
<span class="bu">shift</span>
<span class="kw">done</span>
<span class="co">#</span>
<span class="co"># No matches, then the value is bad</span>
<span class="co">#</span>
<span class="kw">if [[</span> <span class="va">$matches</span> <span class="ot">==</span> 0<span class="kw"> ]]</span>; <span class="kw">then</span>
<span class="bu">return</span> 1
<span class="kw">else</span>
<span class="bu">return</span> 0
<span class="kw">fi</span>
<span class="kw">}</span></code></pre></td></tr></table></div>
<p>The idea is that <code>read_value</code> has been used to get a string from the user of a script. This string may need to be checked to see whether it conforms to a pattern. For example, I have written a script that helps me manage the HPR shows I am in the process of writing. At some point I will have chosen a slot in the queue and want to record that the show number is <code>hpr9876</code> or whatever. I might want even to perform the check against a list of possibilities. I would give the incoming string to <code>check_value</code> and get it to compare against a list of regular expressions.</p>
<ul>
<li>The function takes the value to be checked as the first argument followed by one or more (Bash-style) regular expressions. Note that we use a variant of the usual parameter substitution in the <code>local</code> command here<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a>.</li>
<li>After saving the first argument in the variable called <code>value</code> the next thing the function does (line 15) is to drop the first argument from the parameter list.</li>
<li>Then (lines 16-19) it checks the <code>$#</code> variable (number of arguments) to see if there are any regular expressions. If not it prints an error message and exits with a false value.</li>
<li>A <code>while</code> loop (lines 24-30) then processes each argument until there are no more. Each time it compares the argument (assuming its a regular expression) against the variable called <code>value</code>, incrementing the <code>matches</code> variable if they match. After the test <code>shift</code> is used to drop that argument.</li>
<li>By line 35 the <code>matches</code> variable should be zero if nothing matched or non-zero if there were any matches. The function returns false in the first instance and true otherwise.</li>
</ul>
<p>This might be tested as follows:</p>
<pre><code>$ source read_value.sh
$ source check_value.sh
$ demo () {
name=
until read_value &quot;What is your first name? &quot; name &amp;&amp; test -n &quot;$name&quot;; do
:
done
if check_value &quot;$name&quot; &quot;^[A-Za-z]+$&quot; &quot;^0[Xx][A-Fa-f0-9]+$&quot;; then
echo &quot;Hello $name&quot;
else
echo &quot;That name isn&#39;t valid&quot;
fi
}
$ demo
What is your first name? Herbert
Hello Herbert
$ demo
What is your first name? 0x1101
Hello 0x1101
$ demo
What is your first name? Jim42
That name isn&#39;t valid
$ demo
What is your first name? 0xDEAD42
Hello 0xDEAD42
$ demo
What is your first name? DEAD42
That name isn&#39;t valid</code></pre>
<ul>
<li>This defines a temporary function called <code>demo</code></li>
<li>The variable <code>name</code> is created and made empty</li>
<li>The function <code>read_value</code> is called in an <code>until</code> loop where it requests the callers first name and writes the response to <code>name</code>. It is concatenated<a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a> with a call to the built-in <code>test</code> command using the <code>-n</code> option against the contents of the <code>name</code> variable (substituted into a string). This returns true if the string is not empty, in which case the loop stops. If no name is given the loop will repeat. The loop body consists of a null command (colon).</li>
<li>A call to <code>check_value</code> follows in an <code>if</code> command. If the check returns true then the <code>name</code> variable is echoed, otherwise an error message is given.</li>
<li>The call to <code>check_value</code> uses <code>name</code> as the string to check and it matches against two regular expressions. The first checks for at least one upper or lower case alphabetic character, and nothing else (no spaces in the name allowed). The second expects a hexadecimal number which begins with 0X or 0x followed by at least one hexadecimal digit.</li>
</ul>
<h3 id="the-read_and_check-function">The <strong><code>read_and_check</code></strong> function</h3>
<p>This function uses the two previous functions to read a value and check it is valid. It loops until a valid reply is received or the process is aborted with <em>CTRL-D</em>.</p>
<div class="sourceCode"><table class="sourceCode bash numberLines"><tr class="sourceCode"><td class="lineNumbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="sourceCode"><pre><code class="sourceCode bash"><span class="co">#=== FUNCTION ================================================================</span>
<span class="co"># NAME: read_and_check</span>
<span class="co"># DESCRIPTION: Reads a value (see read_value) and checks it (see check_value)</span>
<span class="co"># against an arbitrary long list of Bash regular expressions</span>
<span class="co"># PARAMETERS: 1 - Prompt string for the read</span>
<span class="co"># 2 - Name of variable to receive the result</span>
<span class="co"># 3 - Default value (optional)</span>
<span class="co"># 4..n - Valid regular expressions</span>
<span class="co"># RETURNS: Nothing</span>
<span class="co">#===============================================================================</span>
<span class="fu">read_and_check ()</span> <span class="kw">{</span>
<span class="bu">local</span> <span class="va">prompt=</span><span class="st">&quot;</span><span class="va">${1:?</span>Usage: read_and_check prompt outputname [default] list_of_regex<span class="va">}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">outputname=</span><span class="st">&quot;</span><span class="va">${2:?</span>Usage: read_and_check prompt outputname [default] list_of_regex<span class="va">}</span><span class="st">&quot;</span>
<span class="bu">local</span> <span class="va">default=</span><span class="st">&quot;</span><span class="va">${3:-}</span><span class="st">&quot;</span>
<span class="kw">if</span> ! <span class="ex">read_value</span> <span class="st">&quot;</span><span class="va">$prompt</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$outputname</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$default</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">then</span>
<span class="bu">return</span> 1
<span class="kw">fi</span>
<span class="bu">shift</span> 3
<span class="kw">until</span> <span class="ex">check_value</span> <span class="st">&quot;</span><span class="va">${!outputname}</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$@</span><span class="st">&quot;</span>
<span class="kw">do</span>
<span class="bu">echo</span> <span class="st">&quot;Invalid input: </span><span class="va">${!outputname}</span><span class="st">&quot;</span>
<span class="kw">if</span> ! <span class="ex">read_value</span> <span class="st">&quot;</span><span class="va">$prompt</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$outputname</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$default</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">then</span>
<span class="bu">return</span> 1
<span class="kw">fi</span>
<span class="kw">done</span>
<span class="bu">return</span> 0
<span class="kw">}</span></code></pre></td></tr></table></div>
<ul>
<li>The function expects a prompt (for <code>read_value</code>), the name of a variable to receive the result, an optional default value and a list of regular expressions.</li>
<li>The first thing the function does is to call <code>read_value</code> (line 16) with the relevant parameters. It does this in an <code>if</code> command because if the prompt is aborted with <em>CTRL-D</em> then a false value is returned, so we abort <code>read_and_check</code> if so.</li>
<li>Next, on line 19, the <code>shift</code> command deletes the first three arguments so we can pass the remainder to <code>check_value</code>.</li>
<li>Then <code>check_value</code> is called in an <code>until</code> loop (lines 20-26) checking the value returned against the list of regular expressions. If the check is passed the loop ends and the function exits. If not then an error message is written and <code>read_value</code> called again (with a check for <em>CTRL-D</em> as before).</li>
<li>A complicating factor in this function is that the local variable <code>outputname</code> contains the name of the (global) variable to receive the value from the user. When we want to examine the contents of this variable we have to use <em>indirect expansion</em> of the form <code>${!outputname}</code>. This means <em>examine the contents of <code>outputname</code> and use what is there as the name of a variable whose value we require</em>. We need to do this when handing the value returned by <code>read_value</code> to <code>check_value</code>, and when reporting that the value is invalid.</li>
</ul>
<p>A test of this function might be as follows:</p>
<pre><code>$ source read_value.sh
$ source check_value.sh
$ source read_and_check.sh
$ read_and_check &quot;Enter slot: &quot; slot &quot;&quot; &#39;^(hpr[0-9]+)?$&#39;
Enter slot: HPR42
Invalid input: HPR42
Enter slot:
$ echo &quot;/$slot/&quot;
//</code></pre>
<ul>
<li>As before the functions are <code>source</code>d to ensure Bash knows about them. You only need to do this once for each one if testing them. If using these functions in a script youd probably just copy all three into the script, which would achieve the same.</li>
<li>Then <code>read_and_check</code> is called to collect an HPR slot number (I use this in my episode preparation toolkit). The variable <code>slot</code> is to hold the result, and there is no default.</li>
<li>The regular expression accepts lower-case hpr followed by a number, or nothing at all.</li>
<li>The first input is rejected because it uses upper-case letters, and the second null input is accepted with no problem.</li>
<li>The echo shows that variable <code>slot</code> is empty.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>These three functions are adequate for my use; I use them in a number of scripts I have written. They are not 100% bullet-proof. For example, if a regular expression is mistyped things could fail messily.</p>
<p>The business of passing information back from a function is messy, though it works. It can be streamlined by the use of <em>nameref</em> variables, which we will look at another time.</p>
<p>Please comment or email me with any improvements or changes you think would make these functions better.</p>
<h2 id="links">Links</h2>
<ul>
<li>Previous HPR episodes in this group <em>Useful Bash functions</em>:
<ul>
<li><a href="http://hackerpublicradio.org/eps.php?id=1757">Part 1</a>: <code>pad</code> and <code>yes_no</code> functions</li>
<li><a href="http://hackerpublicradio.org/eps.php?id=2096">Part 2</a>: <code>yes_no</code> revisited</li>
</ul></li>
<li>Download the <a href="http://hackerpublicradio.org/eps/hpr2448/read_value.sh"><em>read_value</em></a>, <a href="http://hackerpublicradio.org/eps/hpr2448/check_value.sh"><em>check_value</em></a> and <a href="http://hackerpublicradio.org/eps/hpr2448/read_and_check.sh"><em>read_and_check</em></a> functions and the <a href="http://hackerpublicradio.org/eps/hpr2448/trace_of_read_value.txt">trace of <em>read_value</em></a>.</li>
</ul>
<!--
vim: syntax=markdown:ts=8:sw=4:ai:et:tw=78:fo=tcqn:fdm=marker
-->
<section class="footnotes">
<hr />
<ol>
<li id="fn1"><p>The command</p>
<pre><code>local value=&quot;${1?Usage: check_value value list_of_regex}&quot;</code></pre>
<p>is used in this function. The parameter substitution expression <code>${parameter:?word}</code> means that the value of <em>word</em> is used as an error message and the script exits if the <em>parameter</em> is null or unset. Since we might want to use a parameter which is null we use the variant <code>${<em>parameter</em>?<em>word</em>}</code> which merely checks for existence.<a href="#fnref1"></a></p></li>
<li id="fn2"><p>Calling it <em>concatenation</em> is not strictly true. This is what is known as an <em>AND list</em> in Bash. Its generic form is: <code>command1 &amp;&amp; command2</code>, and <em>command2</em> is executed if and only if <em>command1</em> returns an exit status of zero.<a href="#fnref2"></a></p></li>
</ol>
</section>
</article>
</main>
</div>
</body>
</html>