Files
hpr_website/www/eps/hpr2709/hpr2709_full_shownotes.html

242 lines
14 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>Bash Tips - 16 (HPR Show 2709)</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]-->
<link rel="stylesheet" href="http://hackerpublicradio.org/css/hpr.css">
</head>
<body id="home">
<div id="container" class="shadow">
<header>
<h1 class="title">Bash Tips - 16 (HPR Show 2709)</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="#arrays-in-bash">Arrays in Bash</a><ul>
<li><a href="#types-of-arrays">Types of arrays</a></li>
<li><a href="#creating-arrays">Creating arrays</a><ul>
<li><a href="#creating-an-indexed-array">Creating an indexed array</a></li>
<li><a href="#creating-an-associative-array">Creating an associative array</a></li>
</ul></li>
<li><a href="#accessing-array-elements">Accessing array elements</a><ul>
<li><a href="#do-not-omit-the-curly-brackets">Do not omit the curly brackets</a></li>
<li><a href="#with-curly-brackets">With curly brackets</a></li>
<li><a href="#accessing-all-elements-of-an-array">Accessing all elements of an array</a></li>
</ul></li>
</ul></li>
<li><a href="#links">Links</a></li>
</ul>
</nav>
</header>
<h2 id="arrays-in-bash">Arrays in Bash</h2>
<p>This is the first of a small group of shows on the subject of arrays in Bash. It is also the sixteenth show in the <em>Bash Tips</em> sub-series.</p>
<p>We have encountered Bash arrays at various points throughout this sub-series, and have even seen a number of examples, but the subject has never been examined in detail. This group of shows intends to make good this deficiency.</p>
<h3 id="types-of-arrays">Types of arrays</h3>
<p>Bash offers two types of arrays: <em>indexed</em> and <em>associative</em>.</p>
<p>Both types are one-dimensional. Indexed arrays are indexed by positive integers starting at zero. The indices do not have to be sequential (indexed arrays are <em>sparse</em>). Associative arrays are indexed by strings (like the equivalent in Awk). Both array types are unlimited in length and may contain strings.</p>
<h3 id="creating-arrays">Creating arrays</h3>
<p>There are several ways of creating arrays in Bash, and the methods differ slightly between the two types.</p>
<h4 id="creating-an-indexed-array">Creating an indexed array</h4>
<p>It is possible to declare an indexed array just by using a command of the form:</p>
<pre><code>name[subscript]=value</code></pre>
<p>For example:</p>
<pre><code>fruits[0]=&#39;apple&#39;
fruits[1]=&#39;pear&#39;</code></pre>
<p>Here <code>'fruits'</code> is the array name, and the subscripts of the elements being initialised are 0 and 1. The values being set are the strings to the right of each equals sign.</p>
<p>The subscript must be a number or an expression which evaluates to a number:</p>
<pre><code>i=0
fruits[$i]=&#39;apple&#39;
fruits[$((++i))]=&#39;pear&#39;
fruits[$((++i))]=&#39;grape&#39;</code></pre>
<p>Here variable <code>'i'</code> starts at zero and then is incremented for each successive array element (using arithmetic expansion - see episode <a href="http://hackerpublicradio.org/eps/hpr1951" title="Some additional Bash tips">hpr1951</a>).</p>
<p>The same effect can be achieved using a <em>compound assignment</em> of the general form:</p>
<pre><code>name=([subscript1]=value1 [subscript2]=value2)</code></pre>
<p>So, rewriting the last example:</p>
<pre><code>i=0
fruits=([$i]=&#39;apple&#39; [$((++i))]=&#39;pear&#39; [$((++i))]=&#39;grape&#39;)</code></pre>
<p>However, the <code>'[subscript]='</code> part is optional and the whole thing could be written as:</p>
<pre><code>fruits=(&#39;apple&#39; &#39;pear&#39; &#39;grape&#39;)</code></pre>
<p>Using this format the index of the element assigned is the last index assigned to by the statement plus one.</p>
<p>It is even possible to append to an already populated array thus:</p>
<pre><code>fruits+=(&#39;banana&#39;) # append to existing data in an array</code></pre>
<p>Note the use of the <code>'+='</code> operator here. A common mistake is to try and add to an array using the plain <code>'='</code> for the assignment:</p>
<pre><code>fruits=(&#39;banana&#39;) # clear the array and start again</code></pre>
<p>This will empty the array and write <code>'banana'</code> to the zero indexed (first) element.</p>
<p>Another way to define an indexed array is with the <code>'declare'</code> builtin command:</p>
<pre><code>declare -a name</code></pre>
<p>The <code>'-a'</code> option specifies that <code>'name'</code> is an indexed array as opposed to other types of variables that can be created with this command.</p>
<p>There are some interesting features in the <code>'declare'</code> builtin command in the context of arrays which we will look at in a later show.</p>
<h4 id="creating-an-associative-array">Creating an associative array</h4>
<p>As we have seen, with indexed arrays the indices can be derived implicitly (as sequential numbers), but associative arrays use strings as their indices, so these have to be defined explicitly.</p>
<p>Unlike indexed arrays, before working with an associative array it has to be declared explicitly:</p>
<pre><code>declare -A capitals</code></pre>
<p>Then the following syntax initialises an element:</p>
<pre><code>name[subscript]=value</code></pre>
<p>The subscript does not need to be quoted if it contains a space, but other characters in subscripts may need quotes. For example none of the following need to be quoted:</p>
<pre><code>declare -A capitals
capitals[England]=&#39;London&#39;
capitals[Scotland]=&#39;Edinburgh&#39;
capitals[Wales]=&#39;Cardiff&#39;
capitals[Northern Ireland]=&#39;Belfast&#39;</code></pre>
<p>As before the same effect can be achieved using a <em>compound assignment</em>, but, unlike the indexed array, the subscript cannot be omitted:</p>
<pre><code>declare -A capitals
capitals=([England]=&#39;London&#39; [Scotland]=&#39;Edinburgh&#39; [Wales]=&#39;Cardiff&#39; [Northern Ireland]=&#39;Belfast&#39;)</code></pre>
<p>It is also possible to populate the array at declaration time:</p>
<pre><code>declare -A capitals=([England]=&#39;London&#39; [Scotland]=&#39;Edinburgh&#39; [Wales]=&#39;Cardiff&#39; [Northern Ireland]=&#39;Belfast&#39;)</code></pre>
<p>Using non-alphanumeric subscripts will always require quoting:</p>
<pre><code>declare -A chars
chars[&#39;[&#39;]=&#39;open square bracket&#39;
chars[&#39;]&#39;]=&#39;close square bracket&#39;</code></pre>
<h3 id="accessing-array-elements">Accessing array elements</h3>
<p>A simple way of visualising the contents of either type of array is by using <code>'declare -p'</code>. This generates a string which can be used as a command which can be used to rebuild the array if needed.</p>
<p>For example:</p>
<pre><code>$ declare -p fruits capitals chars
declare -a fruits=([0]=&quot;apple&quot; [1]=&quot;pear&quot; [2]=&quot;grape&quot; [3]=&quot;banana&quot;)
declare -A capitals=([&quot;Northern Ireland&quot;]=&quot;Belfast&quot; [England]=&quot;London&quot; [Wales]=&quot;Cardiff&quot; [Scotland]=&quot;Edinburgh&quot;)
declare -A chars=([&quot;[&quot;]=&quot;open square bracket&quot; [&quot;]&quot;]=&quot;close square bracket&quot;)</code></pre>
<p>Note that the ordering of associative array elements is arbitrary. Note also, that the <code>'Northern Ireland'</code> subscript is quoted by Bash and of course the subscripts in the <code>'chars'</code> array are quoted.</p>
<p>The usual way to access array elements is with the following syntax:</p>
<pre><code>${name[subscript]}</code></pre>
<h4 id="do-not-omit-the-curly-brackets">Do not omit the curly brackets</h4>
<p>The curly brackets (braces) are required to avoid conflicts with Bashs filename expansion operators. The expression: <code>'$fruits[1]'</code> will be parsed as the contents of a variable called <code>'fruits'</code> followed by the glob range expression containing the digit <code>'1'</code>.</p>
<p>For the arrays weve been using so far these are the sort of results that result from omitting the braces:</p>
<pre><code>$ echo $fruits[1]
apple[1]
$echo $capitals[1]
[1]
$ ls $fruits[1]
ls: cannot access &#39;apple[1]&#39;: No such file or directory</code></pre>
<p>When an array name is used without a subscript it is interpreted as the element with index zero. For an indexed array this may return an actual value, but for an associative array it depends on whether theres an element with the string 0 as a subscript.</p>
<pre><code>$ declare -A hash=([a]=42 [b]=97 [0]=&#39;What is this?&#39;)
$echo $hash
What is this?</code></pre>
<h4 id="with-curly-brackets">With curly brackets</h4>
<p>Using the braces we see:</p>
<pre><code>$ echo &quot;${fruits[1]}&quot;
pear</code></pre>
<h4 id="accessing-all-elements-of-an-array">Accessing all elements of an array</h4>
<p>There are two special subscripts that return all elements of an array. These are <code>'@'</code> and <code>'*'</code>.</p>
<p>For example:</p>
<pre><code>$ echo &quot;${fruits[@]}&quot;
apple pear grape banana</code></pre>
<p>The difference between <code>'@'</code> and <code>'*'</code> is only apparent when the expression is written in double quotes:</p>
<ul>
<li><code>'*'</code> - the array elements are returned as a single <em>word</em> separated by whatever the first character of the <code>'IFS'</code><a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a> variable is (usually a space).</li>
<li><code>'@'</code> - the array elements are returned as a list of <em>word</em>s.</li>
</ul>
<p>This can be seen when expanding an array in a loop.</p>
<p>The downloadable script in <a href="hpr2709_bash16_ex1.sh">bash16_ex1.sh</a> demonstrates this (especially for fans of <code>/usr/share/dict/words</code>):</p>
<pre><code>$ cat bash16_ex1.sh
#!/bin/bash
#-------------------------------------------------------------------------------
# Example 1 for Bash Tips show 16: the difference between &#39;*&#39; and &#39;@&#39; as array
# subscripts
#-------------------------------------------------------------------------------
#
# Initialise an array
#
declare -a words
#
# Populate the array. Omit capitalised words and the weird possessives.
# [Note: there are better ways of populating arrays as we&#39;ll see in a later
# show]
#
for word in $(grep -E -v &quot;(^[A-Z]|&#39;s$)&quot; /usr/share/dict/words | shuf -n 5); do
words+=( &quot;$word&quot; )
done
#
# Report the array using &#39;*&#39; as the index
#
echo &#39;Using &quot;${words[*]}&quot;&#39;
for word in &quot;${words[*]}&quot;; do
echo &quot;$word&quot;
done
#
# Report the array using &#39;@&#39; as the index
#
echo &#39;Using &quot;${words[@]}&quot;&#39;
for word in &quot;${words[@]}&quot;; do
echo &quot;$word&quot;
done</code></pre>
<p>Invoking the script results in the array of random words being reported in two ways:</p>
<pre><code>Using &quot;${words[*]}&quot;
fatalistic rashes processioned grottoes abusively
Using &quot;${words[@]}&quot;
fatalistic
rashes
processioned
grottoes
abusively
</code></pre>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.gnu.org/software/bash/manual/bash.html">"<em>GNU BASH Reference Manual</em>"</a>
<ul>
<li>Section on <a href="https://www.gnu.org/software/bash/manual/bash.html#Arrays">"Bash Arrays"</a></li>
</ul></li>
</ul>
<!--- -->
<ul>
<li>Linux Documentation Project: <a href="https://www.tldp.org/LDP/abs/html/index.html">Advanced Bash-Scripting Guide</a>
<ul>
<li><a href="https://www.tldp.org/LDP/abs/html/arrays.html">Chapter 27: Arrays</a></li>
</ul></li>
<li><p><a href="http://hackerpublicradio.org/series.php?id=42">HPR series: <em>Bash Scripting</em></a></p></li>
<li>Previous episodes under the heading <em>Bash Tips</em>:
<ol>
<li><a href="http://hackerpublicradio.org/eps/hpr1648">HPR episode 1648 "<em>Bash parameter manipulation</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr1843">HPR episode 1843 "<em>Some Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr1884">HPR episode 1884 "<em>Some more Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr1903">HPR episode 1903 "<em>Some further Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr1951">HPR episode 1951 "<em>Some additional Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2045">HPR episode 2045 "<em>Some other Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2278">HPR episode 2278 "<em>Some supplementary Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2293">HPR episode 2293 "<em>More supplementary Bash tips</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2639">HPR episode 2639 "<em>Some ancillary Bash tips - 9</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2649">HPR episode 2649 "<em>More ancillary Bash tips - 10</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2659">HPR episode 2659 "<em>Further ancillary Bash tips - 11</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2669">HPR episode 2669 "<em>Additional ancillary Bash tips - 12</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2679">HPR episode 2679 "<em>Extra ancillary Bash tips - 13</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2689">HPR episode 2689 "<em>Bash Tips - 14</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2699">HPR episode 2699 "<em>Bash Tips - 15</em>"</a></li>
</ol></li>
</ul>
<!--- -->
<ul>
<li>Resources:
<ul>
<li>Examples: <a href="hpr2709_bash16_ex1.sh">bash16_ex1.sh</a></li>
</ul></li>
</ul>
<section class="footnotes">
<hr />
<ol>
<li id="fn1"><p>I knew Id talked about <code>'IFS'</code> before as I was recording the audio but forgot which show it was. Have a look at the long notes for <a href="http://hackerpublicradio.org/eps/hpr2045" title="Some other Bash tips">hpr2045</a> if you want more information.<a href="#fnref1" class="footnote-back"></a></p></li>
</ol>
</section>
</article>
</main>
</div>
</body>
</html>