Move under www to ease rsync

This commit is contained in:
2025-10-29 10:51:15 +01:00
parent 2bb22c7583
commit 30ad62e938
890 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
<!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 - 18 (HPR Show 2729)</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 - 18 (HPR Show 2729)</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></li>
<li><a href="#more-parameter-expansion-operations-and-arrays">More parameter expansion operations and arrays</a><ul>
<li><a href="#string-replacement">String replacement</a><ul>
<li><a href="#examples-using-simple-variables">Examples using simple variables</a></li>
<li><a href="#examples-using-arrays">Examples using arrays</a></li>
</ul></li>
<li><a href="#changing-case">Changing case</a><ul>
<li><a href="#examples-using-simple-variables-1">Examples using simple variables</a></li>
<li><a href="#examples-using-arrays-1">Examples using arrays</a></li>
</ul></li>
</ul></li>
<li><a href="#examples">Examples</a><ul>
<li><a href="#example-1">Example 1</a></li>
<li><a href="#example-2">Example 2</a></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 third of a small group of shows on the subject of arrays in Bash. It is also the eighteenth show in the <em>Bash Tips</em> sub-series.</p>
<p>In the last show we looked at ways of accessing elements with negative indices and how to concatenate arrays. We then launched into parameter expansion in the context of arrays.</p>
<p>There are a few more parameter expansion operations to look at in this episode, then in the next episode we will look in more depth at the <code>declare</code> built in command and at some of the commands that assist with loading data into arrays.</p>
<h2 id="more-parameter-expansion-operations-and-arrays">More parameter expansion operations and arrays</h2>
<h3 id="string-replacement">String replacement</h3>
<p>This expansion performs a single replacement within a parameter string or repeats the replacement throughout the entire string. It can also perform the same type of operations on array elements.</p>
<p>The syntax is:</p>
<pre><code>${parameter/pattern/string}</code></pre>
<p>The <em>pattern</em> is a <em>glob</em> or <em>extglob</em> pattern. The <em>parameter</em> is expanded and a search carried out for the longest match with <em>pattern</em>, which is replaced with <em>string</em>.</p>
<p>If there is no <em>string</em> the matched pattern is deleted. It is acceptable to simplify the expression if that is so:</p>
<pre><code>${parameter/pattern}</code></pre>
<p>The first character of <em>pattern</em> affects the search:</p>
<ul>
<li><code>'/'</code> - all matches of <em>pattern</em> are replaced by <em>string</em></li>
<li><code>'#'</code> - must match at the beginning of the expanded value of <em>parameter</em></li>
<li><code>'%'</code> - must match at the end of the expanded value of <em>parameter</em></li>
</ul>
<p>If the <em>pattern</em> needs to match one of these characters then it can be escaped by preceding it with a backslash.</p>
<h4 id="examples-using-simple-variables">Examples using simple variables</h4>
<p>Using an old phrase which was used as a typing exercise we simply replace the word <code>'men'</code> by <code>'people'</code>:</p>
<pre><code>$ phrase=&#39;Now is the time for all good men to come to the aid of the party&#39;
$ echo &quot;${phrase/men/people}&quot;
Now is the time for all good people to come to the aid of the party</code></pre>
<p>Using <code>'/'</code> as the first character of <em>pattern</em> we replace all occurrences of <code>'the'</code> by <code>'THE'</code>:</p>
<pre><code>$ echo &quot;${phrase//the/THE}&quot;
Now is THE time for all good men to come to THE aid of THE party</code></pre>
<p>Using an <em>extglob</em> pattern <code>'the'</code> and <code>'to'</code> are replaced by <code>'X'</code>:</p>
<pre><code>$ shopt -s extglob
$ echo &quot;${phrase//@(the|to)/X}&quot;
Now is X time for all good men X come X X aid of X party</code></pre>
<p>Unfortunately it is not possible to vary the replacement string depending on the match. It would be necessary to write something more complex such as a loop to achieve this. See below for an <a href="#examples">example script</a> which performs such a task.</p>
<p>Replace the <code>'N'</code> followed by two letters at the start of the string by <code>'XXX'</code>:</p>
<pre><code>$ echo &quot;${phrase/#N??/XXX}&quot;
XXX is the time for all good men to come to the aid of the party</code></pre>
<p>Matching a pattern which starts with <code>'/', '#', '%'</code> requires escaping the leading character:</p>
<pre><code>$ p2=&#39;#abc#&#39;
$ echo &quot;${p2/\#}&quot;
abc#
$ echo &quot;${p2/%#}&quot;
#abc</code></pre>
<p>Note that in the second case, because the <code>'#'</code> is not the first character it does not need to be escaped.</p>
<h4 id="examples-using-arrays">Examples using arrays</h4>
<p>If the <em>parameter</em> is an array expression using <code>'@'</code> or <code>'*'</code> as an index then the substitution operation is applied to each member of the array in turn.</p>
<p>Here we declare an indexed array whose elements are the words from the example phrase above. We replace the first letter of each element by <code>'X'</code>:</p>
<pre><code>$ declare -a words=( $phrase )
$ echo &quot;${words[@]/?/X}&quot;
Xow Xs Xhe Xime Xor Xll Xood Xen Xo Xome Xo Xhe Xid Xf Xhe Xarty</code></pre>
<p>Here the last letter of each element is replaced by <code>'X'</code>:</p>
<pre><code>$ echo &quot;${words[@]/%?/X}&quot;
NoX iX thX timX foX alX gooX meX tX comX tX thX aiX oX thX partX</code></pre>
<p>If the <em>pattern</em> consists of <code>'#'</code> or <code>'%'</code> on its own it is possible to add text to each element at the start or the end. Here we first add <code>'=&gt; '</code> to the start of each element, then <code>' &lt;='</code> to the end:</p>
<pre><code>$ echo &quot;${words[@]/#/=&gt; }&quot;
=&gt; Now =&gt; is =&gt; the =&gt; time =&gt; for =&gt; all =&gt; good =&gt; men =&gt; to =&gt; come =&gt; to =&gt; the =&gt; aid =&gt; of =&gt; the =&gt; party
$ echo &quot;${words[@]/%/ &lt;=}&quot;
Now &lt;= is &lt;= the &lt;= time &lt;= for &lt;= all &lt;= good &lt;= men &lt;= to &lt;= come &lt;= to &lt;= the &lt;= aid &lt;= of &lt;= the &lt;= party &lt;=</code></pre>
<p>It is possible for the <em>string</em> part to be a reference to another variable (or even a command substitution):</p>
<pre><code>$ echo &quot;${words[@]/#/${words[1]} }&quot;
is Now is is is the is time is for is all is good is men is to is come is to is the is aid is of is the is party</code></pre>
<p>However the value is derived once before the multiple substitutions begin, since this statement is not a script and is executed internally by Bash:</p>
<pre><code>$ echo &quot;${words[@]/#/$RANDOM }&quot;
9559 Now 9559 is 9559 the 9559 time 9559 for 9559 all 9559 good 9559 men 9559 to 9559 come 9559 to 9559 the 9559 aid 9559 of 9559 the 9559 party</code></pre>
<p>The <code>'RANDOM'</code> variable was accessed only once and its value used repeatedly.</p>
<h3 id="changing-case">Changing case</h3>
<p>The final parameter expansion well look at modifies the case of alphabetic characters in <em>parameter</em>. The syntax definition (from the GNU Bash manual) are:</p>
<pre><code>${parameter^pattern}
${parameter^^pattern}
${parameter,pattern}
${parameter,,pattern}</code></pre>
<p>The first pair change to upper case, and the second pair to lower case. It is important to understand the way this expansion expression is described in the manual:</p>
<blockquote>
<p>Each character in the expanded value of <em>parameter</em> is tested against <em>pattern</em>, and, if it matches the pattern, its case is converted. The pattern should not attempt to match more than one character.</p>
</blockquote>
<p>This means that the <em>pattern</em> matches only one character, not a word or similar.</p>
<p>Also, theres this:</p>
<blockquote>
<p>… the <code>'^'</code> and <code>','</code> expansions match and convert only the first character in the expanded value.</p>
</blockquote>
<p>This can catch the unwary - it certainly caught me until I read the description properly!</p>
<p>Where the <code>'^'</code> or <code>','</code> is doubled the case changing operation is performed on every matching character, otherwise it operates on the first character only.</p>
<p>If the <em>pattern</em> is omitted, it is treated like a <code>'?'</code>, which matches every character.</p>
<p>If the <em>parameter</em> is an array variable with <code>'@'</code> or <code>'*'</code> as a subscript then the case changing operations are carried out on each element.</p>
<h4 id="examples-using-simple-variables-1">Examples using simple variables</h4>
<p>Using the phrase from earlier, we can alter the case of every vowel:</p>
<pre><code>$ echo &quot;${phrase^^[aeiou]}&quot;
NOw Is thE tImE fOr All gOOd mEn tO cOmE tO thE AId Of thE pArty</code></pre>
<p>Note that the following expression actually does nothing, as should be apparent from the second extract from the manual above:</p>
<pre><code>$ echo &quot;${phrase^[aeiou]}&quot;
Now is the time for all good men to come to the aid of the party</code></pre>
<p>Contrary to what you might expect, the first vowel is not converted. Instead the <em>pattern</em> is compared with the first letter <code>'N'</code> which it doesnt match - so nothing is done.</p>
<h4 id="examples-using-arrays-1">Examples using arrays</h4>
<p>If we work with the array built earlier and use the vowel pattern:</p>
<pre><code>$ echo &quot;${words[@]^[aeiou]}&quot;
Now Is the time for All good men to come to the Aid Of the party</code></pre>
<p>Now, the pattern has matched any array element that starts with a vowel and has made that leading vowel upper case.</p>
<p>The next example operates on all vowels in each element to give the same result as earlier when working with the variable called <code>'phrase'</code>:</p>
<pre><code>$ echo &quot;${words[@]^^[aeiou]}&quot;
NOw Is thE tImE fOr All gOOd mEn tO cOmE tO thE AId Of thE pArty</code></pre>
<p>This one operates on all non-vowels (consonants):</p>
<pre><code>$ echo &quot;${words[@]^^[^aeiou]}&quot;
NoW iS THe TiMe FoR aLL GooD MeN To CoMe To THe aiD oF THe PaRTY</code></pre>
<p>Dont be tempted to try something like the following:</p>
<pre><code>$ echo &quot;${words[@]^^@(good|men)}&quot;
Now is the time for all good men to come to the aid of the party</code></pre>
<p>Remember the description in the manual: <em>The pattern should not attempt to match more than one character</em>.</p>
<h2 id="examples">Examples</h2>
<h3 id="example-1">Example 1</h3>
<p>I have included a downloadable script <a href="hpr2729_bash18_ex1.sh">bash18_ex1.sh</a> which shows a way of transforming individual words in text the <code>&quot;</code><em>hard way</em><code>&quot;</code>:</p>
<pre><code>#!/bin/bash
#-------------------------------------------------------------------------------
# Example 1 for Bash Tips show 18: transforming words in a string
#
# This is a contrived and overly complex example to show that replacing
# selected words in a phrase by different words is a non-trivial exercise!
#-------------------------------------------------------------------------------
#
# Enable extglob
#
shopt -s extglob
#
# What we&#39;ll work on and where we&#39;ll store the transformed version
#
phrase=&#39;Now is the time for all good men to come to the aid of the party&#39;
newphrase=
#
# How to transform words in the phrase and a place to store the keys
#
declare -A transform=([good]=&#39;bad&#39; [men]=&#39;people&#39; [party]=&#39;Community&#39;)
declare -a keys
#
# Build an extglob pattern from the keys of the associative array
#
keys=( ${!transform[@]} )
targets=&quot;${keys[*]/%/|}&quot; # Each key is followed by a &#39;|&#39;
targets=&quot;${targets%|}&quot; # Strip the last &#39;|&#39;
targets=&quot;${targets// }&quot; # Some spaces got in there too
pattern=&quot;@(${targets})&quot; # Make the pattern at last
#
# Go word by word; if a word matches the pattern replace it by what&#39;s in the
# &#39;transform&#39; array. Because the pattern has been built from the array keys
# we&#39;ll never have the case where a word doesn&#39;t have a transformation.
#
for word in $phrase; do
if [[ $word == $pattern ]]; then
word=${transform[$word]}
fi
newphrase+=&quot;$word &quot;
done
echo &quot;$newphrase&quot;
exit</code></pre>
<p>I have put a lot of comments into the script, but since it uses many of the actions that we have looked at in this series I hope youll be able to understand it with no trouble.</p>
<p>The principle is that just by adding the words to be matched and their replacements into the array <code>'transform'</code> will cause the transformation to happen, because (in this case) the script has generated an <em>extglob</em> pattern from the keys.</p>
<p>Running the script generates the following output:</p>
<pre><code>Now is the time for all bad people to come to the aid of the Community
</code></pre>
<h3 id="example-2">Example 2</h3>
<p>There are other ways of doing this of course; for example the loop could just check if the current word is in the <code>'transform'</code> array and replace the word if so or leave the word alone if not. However, there is no explicit <code>&quot;</code><em>does key X exist in this associative array</em><code>&quot;</code> feature in Bash so its not obvious.</p>
<p>I have included a second downloadable script <a href="hpr2729_bash18_ex2.sh">bash18_ex2.sh</a> which shows a simpler way of transforming individual words in text. This one uses the <code>'-v varname'</code> operator which we looked at in show <a href="http://hackerpublicradio.org/eps/hpr2659" title="Further ancillary Bash tips - 11">2659</a>:</p>
<pre><code>#!/bin/bash
#-------------------------------------------------------------------------------
# Example 2 for Bash Tips show 18: transforming words in a string, second
# simpler method that depends on the &#39;-v&#39; operator
#-------------------------------------------------------------------------------
#
# What we&#39;ll work on and where we&#39;ll store the transformed version
#
phrase=&#39;Now is the time for all good men to come to the aid of the party&#39;
newphrase=
#
# How to transform words in the phrase
#
declare -A transform=([good]=&#39;bad&#39; [men]=&#39;people&#39; [aid]=&#39;assistance&#39;
[party]=&#39;Community&#39;)
#
# Go word by word; if an element with the word as a key exists in the
# &#39;transform&#39; array replace the word by what&#39;s there.
#
for word in $phrase; do
if [[ -v transform[$word] ]]; then
word=${transform[$word]}
fi
newphrase+=&quot;$word &quot;
done
echo &quot;$newphrase&quot;
exit</code></pre>
<p>The <code>'-v varname'</code> operator returns <em>true</em> if the shell variable <em>varname</em> is set (has been assigned a value). Note that the script used <code>'-v transform[$word]'</code> - the name of the array with a subscript.</p>
<p>Running the script (in which the <code>'transform'</code> array is very slightly different) generates the following output:</p>
<pre><code>Now is the time for all bad people to come to the assistance of the Community
</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>
<li>Section on <a href="https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion">"Shell Parameter Expansion"</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>
<li><a href="http://hackerpublicradio.org/eps/hpr2709">HPR episode 2709 "<em>Bash Tips - 16</em>"</a></li>
<li><a href="http://hackerpublicradio.org/eps/hpr2719">HPR episode 2719 "<em>Bash Tips - 17</em>"</a></li>
</ol></li>
</ul>
<!--- -->
<ul>
<li>Resources:
<ul>
<li>Examples: <a href="hpr2729_bash18_ex1.sh">bash18_ex1.sh</a>, <a href="hpr2729_bash18_ex2.sh">bash18_ex2.sh</a></li>
</ul></li>
</ul>
</article>
</main>
</div>
</body>
</html>