diff --git a/LDP/guide/docbook/abs-guide/Du.sh b/LDP/guide/docbook/abs-guide/Du.sh new file mode 100644 index 00000000..d1bdcc54 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/Du.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Du.sh: DOS to UNIX text file converter. + +E_WRONGARGS=65 + +if [ -z "$1" ] +then + echo "Usage: `basename $0` filename-to-convert" + exit $E_WRONGARGS +fi + +NEWFILENAME=$1.unx + +CR='\015' # Carriage return. + # 015 is octal ASCII code for CR. + # Lines in a DOS text file end in a CR-LF. + +tr -d $CR < $1 > $NEWFILENAME +# Delete CR's and write to new file. + +echo "Original DOS text file is \"$1\"." +echo "Converted UNIX text file is \"$NEWFILENAME\"." + +exit 0 + +# Exercise: +# -------- +# Change the above script to convert from UNIX to DOS. diff --git a/LDP/guide/docbook/abs-guide/array-append.bash b/LDP/guide/docbook/abs-guide/array-append.bash new file mode 100644 index 00000000..762fb739 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/array-append.bash @@ -0,0 +1,111 @@ +#! /bin/bash +# array-append.bash + +# Copyright (c) Michael S. Zick, 2003, All rights reserved. +# License: Unrestricted reuse in any form, for any purpose. +# Version: $ID$ +# +# Slightly modified in formatting by M.C. + + +# Array operations are Bash-specific. +# Legacy UNIX /bin/sh lacks equivalents. + + +# Pipe the output of this script to 'more' +#+ so it doesn't scroll off the terminal. + + +# Subscript packed. +declare -a array1=( zero1 one1 two1 ) +# Subscript sparse ([1] is not defined). +declare -a array2=( [0]=zero2 [2]=two2 [3]=three2 ) + +echo +echo '- Confirm that the array is really subscript sparse. -' +echo "Number of elements: 4" # Hard-coded for illustration. +for (( i = 0 ; i < 4 ; i++ )) +do + echo "Element [$i]: ${array2[$i]}" +done +# See also the more general code example in basics-reviewed.bash. + + +declare -a dest + +# Combine (append) two arrays into a third array. +echo +echo 'Conditions: Unquoted, default IFS, All-Elements-Of operator' +echo '- Undefined elements not present, subscripts not maintained. -' +# # The undefined elements do not exist; they are not being dropped. + +dest=( ${array1[@]} ${array2[@]} ) +# dest=${array1[@]}${array2[@]} # Strange results, possibly a bug. + +# Now, list the result. +echo +echo '- - Testing Array Append - -' +cnt=${#dest[@]} + +echo "Number of elements: $cnt" +for (( i = 0 ; i < cnt ; i++ )) +do + echo "Element [$i]: ${dest[$i]}" +done + +# Assign an array to a single array element (twice). +dest[0]=${array1[@]} +dest[1]=${array2[@]} + +# List the result. +echo +echo '- - Testing modified array - -' +cnt=${#dest[@]} + +echo "Number of elements: $cnt" +for (( i = 0 ; i < cnt ; i++ )) +do + echo "Element [$i]: ${dest[$i]}" +done + +# Examine the modified second element. +echo +echo '- - Reassign and list second element - -' + +declare -a subArray=${dest[1]} +cnt=${#subArray[@]} + +echo "Number of elements: $cnt" +for (( i = 0 ; i < cnt ; i++ )) +do + echo "Element [$i]: ${subArray[$i]}" +done + +# The assignment of an entire array to a single element +#+ of another array using the '=${ ... }' array assignment +#+ has converted the array being assigned into a string, +#+ with the elements separated by a space (the first character of IFS). + +# If the original elements didn't contain whitespace . . . +# If the original array isn't subscript sparse . . . +# Then we could get the original array structure back again. + +# Restore from the modified second element. +echo +echo '- - Listing restored element - -' + +declare -a subArray=( ${dest[1]} ) +cnt=${#subArray[@]} + +echo "Number of elements: $cnt" +for (( i = 0 ; i < cnt ; i++ )) +do + echo "Element [$i]: ${subArray[$i]}" +done +echo '- - Do not depend on this behavior. - -' +echo '- - This behavior is subject to change - -' +echo '- - in versions of Bash newer than version 2.05b - -' + +# MSZ: Sorry about any earlier confusion folks. + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/array-assign.bash b/LDP/guide/docbook/abs-guide/array-assign.bash new file mode 100644 index 00000000..7289db31 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/array-assign.bash @@ -0,0 +1,64 @@ +#! /bin/bash +# array-assign.bash + +# Array operations are Bash specific, +#+ hence the ".bash" in the script name. + +# Copyright (c) Michael S. Zick, 2003, All rights reserved. +# License: Unrestricted reuse in any form, for any purpose. +# Version: $ID$ + +# Based on an example provided by Stephane Chazelas +#+ which appeared in the book: Advanced Bash Scripting Guide. + +# Output format of the 'times' command: +# User CPU <space> System CPU +# User CPU of dead children <space> System CPU of dead children + +# Bash has two versions of assigning all elements of an array +#+ to a new array variable. +# Both drop 'null reference' elements +#+ in Bash versions 2.04, 2.05a and 2.05b. +# An additional array assignment that maintains the relationship of +#+ [subscript]=value for arrays may be added to newer versions. + +# Constructs a large array using an internal command, +#+ but anything creating an array of several thousand elements +#+ will do just fine. + +declare -a bigOne=( /dev/* ) +echo +echo 'Conditions: Unquoted, default IFS, All-Elements-Of' +echo "Number of elements in array is ${#bigOne[@]}" + +# set -vx + + + +echo +echo '- - testing: =( ${array[@]} ) - -' +times +declare -a bigTwo=( ${bigOne[@]} ) +# ^ ^ +times + +echo +echo '- - testing: =${array[@]} - -' +times +declare -a bigThree=${bigOne[@]} +# No parentheses this time. +times + +# Comparing the numbers shows that the second form, pointed out +#+ by Stephane Chazelas, is from three to four times faster. + +# I will continue to use the first form in my example descriptions +#+ because I think it is a better illustration of what is happening. + +# The reusable portions of my examples will actual contain +#+ the second form where appropriate because of the speedup. + +# MSZ: Sorry about that earlier oversight folks. + + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/array-ops.sh b/LDP/guide/docbook/abs-guide/array-ops.sh new file mode 100644 index 00000000..8fe7766b --- /dev/null +++ b/LDP/guide/docbook/abs-guide/array-ops.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# array-ops.sh: More fun with arrays. + + +array=( zero one two three four five ) + +echo ${array[0]} # zero +echo ${array:0} # zero + # Parameter expansion of first element, + #+ starting at position # 0 (1st character). +echo ${array:1} # ero + # Parameter expansion of first element, + #+ starting at position # 1 (2nd character). + +echo "--------------" + +echo ${#array[0]} # 4 + # Length of first element of array. +echo ${#array} # 4 + # Length of first element of array. + # (Alternate notation) + +echo ${#array[1]} # 4 + # Length of second element of array. + # Arrays in Bash have zero-based indexing. + +echo ${#array[*]} # 6 + # Number of elements in array. +echo ${#array[@]} # 6 + # Number of elements in array. + +echo "--------------" + +array2=( [0]="first element" [1]="second element" [3]="fourth element" ) + +echo ${array2[0]} # first element +echo ${array2[1]} # second element +echo ${array2[2]} # + # Skipped in initialization, and therefore null. +echo ${array2[3]} # fourth element + + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/array-strops.sh b/LDP/guide/docbook/abs-guide/array-strops.sh new file mode 100644 index 00000000..83ab5c4b --- /dev/null +++ b/LDP/guide/docbook/abs-guide/array-strops.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# array-strops.sh: String operations on arrays. +# Script by Michael Zick. +# Used with permission. + +# In general, any string operation in the ${name ... } notation +#+ can be applied to all string elements in an array +#+ with the ${name[@] ... } or ${name[*] ...} notation. + + +arrayZ=( one two three four five five ) + +echo + +# Trailing Substring Extraction +echo ${arrayZ[@]:0} # one two three four five five + # All elements. + +echo ${arrayZ[@]:1} # two three four five five + # All elements following element[0]. + +echo ${arrayZ[@]:1:2} # two three + # Only the two elements after element[0]. + +echo "-----------------------" + +# Substring Removal +# Removes shortest match from front of string(s), +#+ where the substring is a regular expression. + +echo ${arrayZ[@]#f*r} # one two three five five + # Applied to all elements of the array. + # Matches "four" and removes it. + +# Longest match from front of string(s) +echo ${arrayZ[@]##t*e} # one two four five five + # Applied to all elements of the array. + # Matches "three" and removes it. + +# Shortest match from back of string(s) +echo ${arrayZ[@]%h*e} # one two t four five five + # Applied to all elements of the array. + # Matches "hree" and removes it. + +# Longest match from back of string(s) +echo ${arrayZ[@]%%t*e} # one two four five five + # Applied to all elements of the array. + # Matches "three" and removes it. + +echo "-----------------------" + +# Substring Replacement + +# Replace first occurance of substring with replacement +echo ${arrayZ[@]/fiv/XYZ} # one two three four XYZe XYZe + # Applied to all elements of the array. + +# Replace all occurances of substring +echo ${arrayZ[@]//iv/YY} # one two three four fYYe fYYe + # Applied to all elements of the array. + +# Delete all occurances of substring +# Not specifing a replacement means 'delete' +echo ${arrayZ[@]//fi/} # one two three four ve ve + # Applied to all elements of the array. + +# Replace front-end occurances of substring +echo ${arrayZ[@]/#fi/XY} # one two three four XYve XYve + # Applied to all elements of the array. + +# Replace back-end occurances of substring +echo ${arrayZ[@]/%ve/ZZ} # one two three four fiZZ fiZZ + # Applied to all elements of the array. + +echo ${arrayZ[@]/%o/XX} # one twXX three four five five + # Why? + +echo "-----------------------" + + +# Before reaching for awk (or anything else) +# Recall: +# $( ... ) is a function call. +# Functions run as a sub-process. +# Functions write their output to stdout. +# Assignment reads the function's stdout. +# The name[@] notation specifies a "for-each" operation. + +newstr() { + echo -n "!!!" +} + +echo ${arrayZ[@]/%e/$(newstr)} +# on!!! two thre!!! four fiv!!! fiv!!! +# Q.E.D: The replacement action is an 'assignment.' + +# Accessing the "For-Each" +echo ${arrayZ[@]//*/$(newstr optional_arguments)} +# Now, if Bash would just pass the matched string as $0 +#+ to the function being called . . . + +echo + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/basics-reviewed.bash b/LDP/guide/docbook/abs-guide/basics-reviewed.bash new file mode 100644 index 00000000..740f64c0 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/basics-reviewed.bash @@ -0,0 +1,999 @@ +#!/bin/bash +# basics-reviewed.bash + +# File extension == *.bash == specific to Bash + +# Copyright (c) Michael S. Zick, 2003; All rights reserved. +# License: Use in any form, for any purpose. +# Revision: $ID$ +# +# Edited for layout by M.C. +# (author of the "Advanced Bash Scripting Guide") + + +# This script tested under Bash versions 2.04, 2.05a and 2.05b. +# It may not work with earlier versions. +# This demonstration script generates one --intentional-- +#+ "command not found" error message. See line 394. + +# The current Bash maintainer, Chet Ramey, has fixed the items noted +#+ for an upcoming version of Bash. + + + + ###-------------------------------------------### + ### Pipe the output of this script to 'more' ### + ###+ else it will scroll off the page. ### + ### ### + ### You may also redirect its output ### + ###+ to a file for examination. ### + ###-------------------------------------------### + + + +# Most of the following points are described at length in +#+ the text of the foregoing "Advanced Bash Scripting Guide." +# This demonstration script is mostly just a reorganized presentation. +# -- msz + +# Variables are not typed unless otherwise specified. + +# Variables are named. Names must contain a non-digit. +# File descriptor names (as in, for example: 2>&1) +#+ contain ONLY digits. + +# Parameters and Bash array elements are numbered. +# (Parameters are very similar to Bash arrays.) + +# A variable name may be undefined (null reference). +unset VarNull + +# A variable name may be defined but empty (null contents). +VarEmpty='' # Two, adjacent, single quotes. + +# A variable name my be defined and non-empty +VarSomething='Literal' + +# A variable may contain: +# * A whole number as a signed 32-bit (or larger) integer +# * A string +# A variable may also be an array. + +# A string may contain embedded blanks and may be treated +#+ as if it where a function name with optional arguments. + +# The names of variables and the names of functions +#+ are in different namespaces. + + +# A variable may be defined as a Bash array either explicitly or +#+ implicitly by the syntax of the assignment statement. +# Explicit: +declare -a ArrayVar + + + +# The echo command is a built-in. +echo $VarSomething + +# The printf command is a built-in. +# Translate %s as: String-Format +printf %s $VarSomething # No linebreak specified, none output. +echo # Default, only linebreak output. + + + + +# The Bash parser word breaks on whitespace. +# Whitespace, or the lack of it is significant. +# (This holds true in general; there are, of course, exceptions.) + + + + +# Translate the DOLLAR_SIGN character as: Content-Of. + +# Extended-Syntax way of writing Content-Of: +echo ${VarSomething} + +# The ${ ... } Extended-Syntax allows more than just the variable +#+ name to be specified. +# In general, $VarSomething can always be written as: ${VarSomething}. + +# Call this script with arguments to see the following in action. + + + +# Outside of double-quotes, the special characters @ and * +#+ specify identical behavior. +# May be pronounced as: All-Elements-Of. + +# Without specification of a name, they refer to the +#+ pre-defined parameter Bash-Array. + + + +# Glob-Pattern references +echo $* # All parameters to script or function +echo ${*} # Same + +# Bash disables filename expansion for Glob-Patterns. +# Only character matching is active. + + +# All-Elements-Of references +echo $@ # Same as above +echo ${@} # Same as above + + + + +# Within double-quotes, the behavior of Glob-Pattern references +#+ depends on the setting of IFS (Input Field Separator). +# Within double-quotes, All-Elements-Of references behave the same. + + +# Specifying only the name of a variable holding a string refers +#+ to all elements (characters) of a string. + + +# To specify an element (character) of a string, +#+ the Extended-Syntax reference notation (see below) MAY be used. + + + + +# Specifying only the name of a Bash array references +#+ the subscript zero element, +#+ NOT the FIRST DEFINED nor the FIRST WITH CONTENTS element. + +# Additional qualification is needed to reference other elements, +#+ which means that the reference MUST be written in Extended-Syntax. +# The general form is: ${name[subscript]}. + +# The string forms may also be used: ${name:subscript} +#+ for Bash-Arrays when referencing the subscript zero element. + + +# Bash-Arrays are implemented internally as linked lists, +#+ not as a fixed area of storage as in some programming languages. + + +# Characteristics of Bash arrays (Bash-Arrays): +# -------------------------------------------- + +# If not otherwise specified, Bash-Array subscripts begin with +#+ subscript number zero. Literally: [0] +# This is called zero-based indexing. +### +# If not otherwise specified, Bash-Arrays are subscript packed +#+ (sequential subscripts without subscript gaps). +### +# Negative subscripts are not allowed. +### +# Elements of a Bash-Array need not all be of the same type. +### +# Elements of a Bash-Array may be undefined (null reference). +# That is, a Bash-Array my be "subscript sparse." +### +# Elements of a Bash-Array may be defined and empty (null contents). +### +# Elements of a Bash-Array may contain: +# * A whole number as a signed 32-bit (or larger) integer +# * A string +# * A string formated so that it appears to be a function name +# + with optional arguments +### +# Defined elements of a Bash-Array may be undefined (unset). +# That is, a subscript packed Bash-Array may be changed +# + into a subscript sparse Bash-Array. +### +# Elements may be added to a Bash-Array by defining an element +#+ not previously defined. +### +# For these reasons, I have been calling them "Bash-Arrays". +# I'll return to the generic term "array" from now on. +# -- msz + + + + +# Demo time -- initialize the previously declared ArrayVar as a +#+ sparse array. +# (The 'unset ... ' is just documentation here.) + +unset ArrayVar[0] # Just for the record +ArrayVar[1]=one # Unquoted literal +ArrayVar[2]='' # Defined, and empty +unset ArrayVar[3] # Just for the record +ArrayVar[4]='four' # Quoted literal + + + +# Translate the %q format as: Quoted-Respecting-IFS-Rules. +echo +echo '- - Outside of double-quotes - -' +### +printf %q ${ArrayVar[*]} # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'${ArrayVar[*]} +### +printf %q ${ArrayVar[@]} # All-Elements-Of +echo +echo 'echo command:'${ArrayVar[@]} + +# The use of double-quotes may be translated as: Enable-Substitution. + +# There are five cases recognized for the IFS setting. + +echo +echo '- - Within double-quotes - Default IFS of space-tab-newline - -' +IFS=$'\x20'$'\x09'$'\x0A' # These three bytes, + #+ in exactly this order. + + +printf %q "${ArrayVar[*]}" # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[*]}" +### +printf %q "${ArrayVar[@]}" # All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[@]}" + + +echo +echo '- - Within double-quotes - First character of IFS is ^ - -' +# Any printing, non-whitespace character should do the same. +IFS='^'$IFS # ^ + space tab newline +### +printf %q "${ArrayVar[*]}" # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[*]}" +### +printf %q "${ArrayVar[@]}" # All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[@]}" + + +echo +echo '- - Within double-quotes - Without whitespace in IFS - -' +IFS='^:%!' +### +printf %q "${ArrayVar[*]}" # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[*]}" +### +printf %q "${ArrayVar[@]}" # All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[@]}" + + +echo +echo '- - Within double-quotes - IFS set and empty - -' +IFS='' +### +printf %q "${ArrayVar[*]}" # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[*]}" +### +printf %q "${ArrayVar[@]}" # All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[@]}" + + +echo +echo '- - Within double-quotes - IFS undefined - -' +unset IFS +### +printf %q "${ArrayVar[*]}" # Glob-Pattern All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[*]}" +### +printf %q "${ArrayVar[@]}" # All-Elements-Of +echo +echo 'echo command:'"${ArrayVar[@]}" + + +# Put IFS back to the default. +# Default is exactly these three bytes. +IFS=$'\x20'$'\x09'$'\x0A' # In exactly this order. + +# Interpretation of the above outputs: +# A Glob-Pattern is I/O; the setting of IFS matters. +### +# An All-Elements-Of does not consider IFS settings. +### +# Note the different output using the echo command and the +#+ quoted format operator of the printf command. + + +# Recall: +# Parameters are similar to arrays and have the similar behaviors. +### +# The above examples demonstrate the possible variations. +# To retain the shape of a sparse array, additional script +#+ programming is required. +### +# The source code of Bash has a routine to output the +#+ [subscript]=value array assignment format. +# As of version 2.05b, that routine is not used, +#+ but that might change in future releases. + + + +# The length of a string, measured in non-null elements (characters): +echo +echo '- - Non-quoted references - -' +echo 'Non-Null character count: '${#VarSomething}' characters.' + +# test='Lit'$'\x00''eral' # $'\x00' is a null character. +# echo ${#test} # See that? + + + +# The length of an array, measured in defined elements, +#+ including null content elements. +echo +echo 'Defined content count: '${#ArrayVar[@]}' elements.' +# That is NOT the maximum subscript (4). +# That is NOT the range of the subscripts (1 . . 4 inclusive). +# It IS the length of the linked list. +### +# Both the maximum subscript and the range of the subscripts may +#+ be found with additional script programming. + +# The length of a string, measured in non-null elements (characters): +echo +echo '- - Quoted, Glob-Pattern references - -' +echo 'Non-Null character count: '"${#VarSomething}"' characters.' + +# The length of an array, measured in defined elements, +#+ including null-content elements. +echo +echo 'Defined element count: '"${#ArrayVar[*]}"' elements.' + +# Interpretation: Substitution does not effect the ${# ... } operation. +# Suggestion: +# Always use the All-Elements-Of character +#+ if that is what is intended (independence from IFS). + + + +# Define a simple function. +# I include an underscore in the name +#+ to make it distinctive in the examples below. +### +# Bash separates variable names and function names +#+ in different namespaces. +# The Mark-One eyeball isn't that advanced. +### +_simple() { + echo -n 'SimpleFunc'$@ # Newlines are swallowed in +} #+ result returned in any case. + + +# The ( ... ) notation invokes a command or function. +# The $( ... ) notation is pronounced: Result-Of. + + +# Invoke the function _simple +echo +echo '- - Output of function _simple - -' +_simple # Try passing arguments. +echo +# or +(_simple) # Try passing arguments. +echo + +echo '- Is there a variable of that name? -' +echo $_simple not defined # No variable by that name. + +# Invoke the result of function _simple (Error msg intended) + +### +$(_simple) # Gives an error message: +# line 394: SimpleFunc: command not found +# --------------------------------------- + +echo +### + +# The first word of the result of function _simple +#+ is neither a valid Bash command nor the name of a defined function. +### +# This demonstrates that the output of _simple is subject to evaluation. +### +# Interpretation: +# A function can be used to generate in-line Bash commands. + + +# A simple function where the first word of result IS a bash command: +### +_print() { + echo -n 'printf %q '$@ +} + +echo '- - Outputs of function _print - -' +_print parm1 parm2 # An Output NOT A Command. +echo + +$(_print parm1 parm2) # Executes: printf %q parm1 parm2 + # See above IFS examples for the + #+ various possibilities. +echo + +$(_print $VarSomething) # The predictable result. +echo + + + +# Function variables +# ------------------ + +echo +echo '- - Function variables - -' +# A variable may represent a signed integer, a string or an array. +# A string may be used like a function name with optional arguments. + +# set -vx # Enable if desired +declare -f funcVar #+ in namespace of functions + +funcVar=_print # Contains name of function. +$funcVar parm1 # Same as _print at this point. +echo + +funcVar=$(_print ) # Contains result of function. +$funcVar # No input, No output. +$funcVar $VarSomething # The predictable result. +echo + +funcVar=$(_print $VarSomething) # $VarSomething replaced HERE. +$funcVar # The expansion is part of the +echo #+ variable contents. + +funcVar="$(_print $VarSomething)" # $VarSomething replaced HERE. +$funcVar # The expansion is part of the +echo #+ variable contents. + +# The difference between the unquoted and the double-quoted versions +#+ above can be seen in the "protect_literal.sh" example. +# The first case above is processed as two, unquoted, Bash-Words. +# The second case above is processed as one, quoted, Bash-Word. + + + + +# Delayed replacement +# ------------------- + +echo +echo '- - Delayed replacement - -' +funcVar="$(_print '$VarSomething')" # No replacement, single Bash-Word. +eval $funcVar # $VarSomething replaced HERE. +echo + +VarSomething='NewThing' +eval $funcVar # $VarSomething replaced HERE. +echo + +# Restore the original setting trashed above. +VarSomething=Literal + +# There are a pair of functions demonstrated in the +#+ "protect_literal.sh" and "unprotect_literal.sh" examples. +# These are general purpose functions for delayed replacement literals +#+ containing variables. + + + + + +# REVIEW: +# ------ + +# A string can be considered a Classic-Array of elements (characters). +# A string operation applies to all elements (characters) of the string +#+ (in concept, anyway). +### +# The notation: ${array_name[@]} represents all elements of the +#+ Bash-Array: array_name. +### +# The Extended-Syntax string operations can be applied to all +#+ elements of an array. +### +# This may be thought of as a For-Each operation on a vector of strings. +### +# Parameters are similar to an array. +# The initialization of a parameter array for a script +#+ and a parameter array for a function only differ +#+ in the initialization of ${0}, which never changes its setting. +### +# Subscript zero of the script's parameter array contains +#+ the name of the script. +### +# Subscript zero of a function's parameter array DOES NOT contain +#+ the name of the function. +# The name of the current function is accessed by the $FUNCNAME variable. +### +# A quick, review list follows (quick, not short). + +echo +echo '- - Test (but not change) - -' +echo '- null reference -' +echo -n ${VarNull-'NotSet'}' ' # NotSet +echo ${VarNull} # NewLine only +echo -n ${VarNull:-'NotSet'}' ' # NotSet +echo ${VarNull} # Newline only + +echo '- null contents -' +echo -n ${VarEmpty-'Empty'}' ' # Only the space +echo ${VarEmpty} # Newline only +echo -n ${VarEmpty:-'Empty'}' ' # Empty +echo ${VarEmpty} # Newline only + +echo '- contents -' +echo ${VarSomething-'Content'} # Literal +echo ${VarSomething:-'Content'} # Literal + +echo '- Sparse Array -' +echo ${ArrayVar[@]-'not set'} + +# ASCII-Art time +# State Y==yes, N==no +# - :- +# Unset Y Y ${# ... } == 0 +# Empty N Y ${# ... } == 0 +# Contents N N ${# ... } > 0 + +# Either the first and/or the second part of the tests +#+ may be a command or a function invocation string. +echo +echo '- - Test 1 for undefined - -' +declare -i t +_decT() { + t=$t-1 +} + +# Null reference, set: t == -1 +t=${#VarNull} # Results in zero. +${VarNull- _decT } # Function executes, t now -1. +echo $t + +# Null contents, set: t == 0 +t=${#VarEmpty} # Results in zero. +${VarEmpty- _decT } # _decT function NOT executed. +echo $t + +# Contents, set: t == number of non-null characters +VarSomething='_simple' # Set to valid function name. +t=${#VarSomething} # non-zero length +${VarSomething- _decT } # Function _simple executed. +echo $t # Note the Append-To action. + +# Exercise: clean up that example. +unset t +unset _decT +VarSomething=Literal + +echo +echo '- - Test and Change - -' +echo '- Assignment if null reference -' +echo -n ${VarNull='NotSet'}' ' # NotSet NotSet +echo ${VarNull} +unset VarNull + +echo '- Assignment if null reference -' +echo -n ${VarNull:='NotSet'}' ' # NotSet NotSet +echo ${VarNull} +unset VarNull + +echo '- No assignment if null contents -' +echo -n ${VarEmpty='Empty'}' ' # Space only +echo ${VarEmpty} +VarEmpty='' + +echo '- Assignment if null contents -' +echo -n ${VarEmpty:='Empty'}' ' # Empty Empty +echo ${VarEmpty} +VarEmpty='' + +echo '- No change if already has contents -' +echo ${VarSomething='Content'} # Literal +echo ${VarSomething:='Content'} # Literal + + +# "Subscript sparse" Bash-Arrays +### +# Bash-Arrays are subscript packed, beginning with +#+ subscript zero unless otherwise specified. +### +# The initialization of ArrayVar was one way +#+ to "otherwise specify". Here is the other way: +### +echo +declare -a ArraySparse +ArraySparse=( [1]=one [2]='' [4]='four' ) +# [0]=null reference, [2]=null content, [3]=null reference + +echo '- - Array-Sparse List - -' +# Within double-quotes, default IFS, Glob-Pattern + +IFS=$'\x20'$'\x09'$'\x0A' +printf %q "${ArraySparse[*]}" +echo + +# Note that the output does not distinguish between "null content" +#+ and "null reference". +# Both print as escaped whitespace. +### +# Note also that the output does NOT contain escaped whitespace +#+ for the "null reference(s)" prior to the first defined element. +### +# This behavior of 2.04, 2.05a and 2.05b has been reported +#+ and may change in a future version of Bash. + +# To output a sparse array and maintain the [subscript]=value +#+ relationship without change requires a bit of programming. +# One possible code fragment: +### +# local l=${#ArraySparse[@]} # Count of defined elements +# local f=0 # Count of found subscripts +# local i=0 # Subscript to test +( # Anonymous in-line function + for (( l=${#ArraySparse[@]}, f = 0, i = 0 ; f < l ; i++ )) + do + # 'if defined then...' + ${ArraySparse[$i]+ eval echo '\ ['$i']='${ArraySparse[$i]} ; (( f++ )) } + done +) + +# The reader coming upon the above code fragment cold +#+ might want to review "command lists" and "multiple commands on a line" +#+ in the text of the foregoing "Advanced Bash Scripting Guide." +### +# Note: +# The "read -a array_name" version of the "read" command +#+ begins filling array_name at subscript zero. +# ArraySparse does not define a value at subscript zero. +### +# The user needing to read/write a sparse array to either +#+ external storage or a communications socket must invent +#+ a read/write code pair suitable for their purpose. +### +# Exercise: clean it up. + +unset ArraySparse + +echo +echo '- - Conditional alternate (But not change)- -' +echo '- No alternate if null reference -' +echo -n ${VarNull+'NotSet'}' ' +echo ${VarNull} +unset VarNull + +echo '- No alternate if null reference -' +echo -n ${VarNull:+'NotSet'}' ' +echo ${VarNull} +unset VarNull + +echo '- Alternate if null contents -' +echo -n ${VarEmpty+'Empty'}' ' # Empty +echo ${VarEmpty} +VarEmpty='' + +echo '- No alternate if null contents -' +echo -n ${VarEmpty:+'Empty'}' ' # Space only +echo ${VarEmpty} +VarEmpty='' + +echo '- Alternate if already has contents -' + +# Alternate literal +echo -n ${VarSomething+'Content'}' ' # Content Literal +echo ${VarSomething} + +# Invoke function +echo -n ${VarSomething:+ $(_simple) }' ' # SimpleFunc Literal +echo ${VarSomething} +echo + +echo '- - Sparse Array - -' +echo ${ArrayVar[@]+'Empty'} # An array of 'Empty'(ies) +echo + +echo '- - Test 2 for undefined - -' + +declare -i t +_incT() { + t=$t+1 +} + +# Note: +# This is the same test used in the sparse array +#+ listing code fragment. + +# Null reference, set: t == -1 +t=${#VarNull}-1 # Results in minus-one. +${VarNull+ _incT } # Does not execute. +echo $t' Null reference' + +# Null contents, set: t == 0 +t=${#VarEmpty}-1 # Results in minus-one. +${VarEmpty+ _incT } # Executes. +echo $t' Null content' + +# Contents, set: t == (number of non-null characters) +t=${#VarSomething}-1 # non-null length minus-one +${VarSomething+ _incT } # Executes. +echo $t' Contents' + +# Exercise: clean up that example. +unset t +unset _incT + +# ${name?err_msg} ${name:?err_msg} +# These follow the same rules but always exit afterwards +#+ if an action is specified following the question mark. +# The action following the question mark may be a literal +#+ or a function result. +### +# ${name?} ${name:?} are test-only, the return can be tested. + + + + +# Element operations +# ------------------ + +echo +echo '- - Trailing sub-element selection - -' + +# Strings, Arrays and Positional parameters + +# Call this script with multiple arguments +#+ to see the parameter selections. + +echo '- All -' +echo ${VarSomething:0} # all non-null characters +echo ${ArrayVar[@]:0} # all elements with content +echo ${@:0} # all parameters with content; + # ignoring parameter[0] + +echo +echo '- All after -' +echo ${VarSomething:1} # all non-null after character[0] +echo ${ArrayVar[@]:1} # all after element[0] with content +echo ${@:2} # all after param[1] with content + +echo +echo '- Range after -' +echo ${VarSomething:4:3} # ral + # Three characters after + # character[3] + +echo '- Sparse array gotch -' +echo ${ArrayVar[@]:1:2} # four - The only element with content. + # Two elements after (if that many exist). + # the FIRST WITH CONTENTS + #+ (the FIRST WITH CONTENTS is being + #+ considered as if it + #+ were subscript zero). +# Executed as if Bash considers ONLY array elements with CONTENT +# printf %q "${ArrayVar[@]:0:3}" # Try this one + +# In versions 2.04, 2.05a and 2.05b, +#+ Bash does not handle sparse arrays as expected using this notation. +# +# The current Bash maintainer, Chet Ramey, has corrected this +#+ for an upcoming version of Bash. + + +echo '- Non-sparse array -' +echo ${@:2:2} # Two parameters following parameter[1] + +# New victims for string vector examples: +stringZ=abcABC123ABCabc +arrayZ=( abcabc ABCABC 123123 ABCABC abcabc ) +sparseZ=( [1]='abcabc' [3]='ABCABC' [4]='' [5]='123123' ) + +echo +echo ' - - Victim string - -'$stringZ'- - ' +echo ' - - Victim array - -'${arrayZ[@]}'- - ' +echo ' - - Sparse array - -'${sparseZ[@]}'- - ' +echo ' - [0]==null ref, [2]==null ref, [4]==null content - ' +echo ' - [1]=abcabc [3]=ABCABC [5]=123123 - ' +echo ' - non-null-reference count: '${#sparseZ[@]}' elements' + +echo +echo '- - Prefix sub-element removal - -' +echo '- - Glob-Pattern match must include the first character. - -' +echo '- - Glob-Pattern may be a literal or a function result. - -' +echo + + +# Function returning a simple, Literal, Glob-Pattern +_abc() { + echo -n 'abc' +} + +echo '- Shortest prefix -' +echo ${stringZ#123} # Unchanged (not a prefix). +echo ${stringZ#$(_abc)} # ABC123ABCabc +echo ${arrayZ[@]#abc} # Applied to each element. + +# Fixed by Chet Ramey for an upcoming version of Bash. +# echo ${sparseZ[@]#abc} # Version-2.05b core dumps. + +# The -it would be nice- First-Subscript-Of +# echo ${#sparseZ[@]#*} # This is NOT valid Bash. + +echo +echo '- Longest prefix -' +echo ${stringZ##1*3} # Unchanged (not a prefix) +echo ${stringZ##a*C} # abc +echo ${arrayZ[@]##a*c} # ABCABC 123123 ABCABC + +# Fixed by Chet Ramey for an upcoming version of Bash +# echo ${sparseZ[@]##a*c} # Version-2.05b core dumps. + +echo +echo '- - Suffix sub-element removal - -' +echo '- - Glob-Pattern match must include the last character. - -' +echo '- - Glob-Pattern may be a literal or a function result. - -' +echo +echo '- Shortest suffix -' +echo ${stringZ%1*3} # Unchanged (not a suffix). +echo ${stringZ%$(_abc)} # abcABC123ABC +echo ${arrayZ[@]%abc} # Applied to each element. + +# Fixed by Chet Ramey for an upcoming version of Bash. +# echo ${sparseZ[@]%abc} # Version-2.05b core dumps. + +# The -it would be nice- Last-Subscript-Of +# echo ${#sparseZ[@]%*} # This is NOT valid Bash. + +echo +echo '- Longest suffix -' +echo ${stringZ%%1*3} # Unchanged (not a suffix) +echo ${stringZ%%b*c} # a +echo ${arrayZ[@]%%b*c} # a ABCABC 123123 ABCABC a + +# Fixed by Chet Ramey for an upcoming version of Bash. +# echo ${sparseZ[@]%%b*c} # Version-2.05b core dumps. + +echo +echo '- - Sub-element replacement - -' +echo '- - Sub-element at any location in string. - -' +echo '- - First specification is a Glob-Pattern - -' +echo '- - Glob-Pattern may be a literal or Glob-Pattern function result. - -' +echo '- - Second specification may be a literal or function result. - -' +echo '- - Second specification may be unspecified. Pronounce that' +echo ' as: Replace-With-Nothing (Delete) - -' +echo + + + +# Function returning a simple, Literal, Glob-Pattern +_123() { + echo -n '123' +} + +echo '- Replace first occurrence -' +echo ${stringZ/$(_123)/999} # Changed (123 is a component). +echo ${stringZ/ABC/xyz} # xyzABC123ABCabc +echo ${arrayZ[@]/ABC/xyz} # Applied to each element. +echo ${sparseZ[@]/ABC/xyz} # Works as expected. + +echo +echo '- Delete first occurrence -' +echo ${stringZ/$(_123)/} +echo ${stringZ/ABC/} +echo ${arrayZ[@]/ABC/} +echo ${sparseZ[@]/ABC/} + +# The replacement need not be a literal, +#+ since the result of a function invocation is allowed. +# This is general to all forms of replacement. +echo +echo '- Replace first occurrence with Result-Of -' +echo ${stringZ/$(_123)/$(_simple)} # Works as expected. +echo ${arrayZ[@]/ca/$(_simple)} # Applied to each element. +echo ${sparseZ[@]/ca/$(_simple)} # Works as expected. + +echo +echo '- Replace all occurrences -' +echo ${stringZ//[b2]/X} # X-out b's and 2's +echo ${stringZ//abc/xyz} # xyzABC123ABCxyz +echo ${arrayZ[@]//abc/xyz} # Applied to each element. +echo ${sparseZ[@]//abc/xyz} # Works as expected. + +echo +echo '- Delete all occurrences -' +echo ${stringZ//[b2]/} +echo ${stringZ//abc/} +echo ${arrayZ[@]//abc/} +echo ${sparseZ[@]//abc/} + +echo +echo '- - Prefix sub-element replacement - -' +echo '- - Match must include the first character. - -' +echo + +echo '- Replace prefix occurrences -' +echo ${stringZ/#[b2]/X} # Unchanged (neither is a prefix). +echo ${stringZ/#$(_abc)/XYZ} # XYZABC123ABCabc +echo ${arrayZ[@]/#abc/XYZ} # Applied to each element. +echo ${sparseZ[@]/#abc/XYZ} # Works as expected. + +echo +echo '- Delete prefix occurrences -' +echo ${stringZ/#[b2]/} +echo ${stringZ/#$(_abc)/} +echo ${arrayZ[@]/#abc/} +echo ${sparseZ[@]/#abc/} + +echo +echo '- - Suffix sub-element replacement - -' +echo '- - Match must include the last character. - -' +echo + +echo '- Replace suffix occurrences -' +echo ${stringZ/%[b2]/X} # Unchanged (neither is a suffix). +echo ${stringZ/%$(_abc)/XYZ} # abcABC123ABCXYZ +echo ${arrayZ[@]/%abc/XYZ} # Applied to each element. +echo ${sparseZ[@]/%abc/XYZ} # Works as expected. + +echo +echo '- Delete suffix occurrences -' +echo ${stringZ/%[b2]/} +echo ${stringZ/%$(_abc)/} +echo ${arrayZ[@]/%abc/} +echo ${sparseZ[@]/%abc/} + +echo +echo '- - Special cases of null Glob-Pattern - -' +echo + +echo '- Prefix all -' +# null substring pattern means 'prefix' +echo ${stringZ/#/NEW} # NEWabcABC123ABCabc +echo ${arrayZ[@]/#/NEW} # Applied to each element. +echo ${sparseZ[@]/#/NEW} # Applied to null-content also. + # That seems reasonable. + +echo +echo '- Suffix all -' +# null substring pattern means 'suffix' +echo ${stringZ/%/NEW} # abcABC123ABCabcNEW +echo ${arrayZ[@]/%/NEW} # Applied to each element. +echo ${sparseZ[@]/%/NEW} # Applied to null-content also. + # That seems reasonable. + +echo +echo '- - Special case For-Each Glob-Pattern - -' +echo '- - - - This is a nice-to-have dream - - - -' +echo + +_GenFunc() { + echo -n ${0} # Illustration only. + # Actually, that would be an arbitrary computation. +} + +# All occurrences, matching the AnyThing pattern. +# Currently //*/ does not match null-content nor null-reference. +# /#/ and /%/ does match null-content but not null-reference. +echo ${sparseZ[@]//*/$(_GenFunc)} + + +# A possible syntax would be to make +#+ the parameter notation used within this construct mean: +# ${1} - The full element +# ${2} - The prefix, if any, to the matched sub-element +# ${3} - The matched sub-element +# ${4} - The suffix, if any, to the matched sub-element +# +# echo ${sparseZ[@]//*/$(_GenFunc ${3})} # Same as ${1} here. +# Perhaps it will be implemented in a future version of Bash. + + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/letter-count.sh b/LDP/guide/docbook/abs-guide/letter-count.sh new file mode 100644 index 00000000..f8e8c0a7 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/letter-count.sh @@ -0,0 +1,60 @@ +#! /bin/sh +# letter-count.sh: Counting letter occurrences in a text file. +# +# Script by nyal (nyal@voila.fr). +# Used with permission. +# Recommented by document author. + + +INIT_TAB_AWK="" +# Parameter to initialize awk script. +count_case=0 +FILE_PARSE=$1 + +E_PARAMERR=65 + +usage() +{ + echo "Usage: letter-count.sh file letters" 2>&1 + # For example: ./letter-count.sh filename.txt a b c + exit $E_PARAMERR # Not enough arguments passed to script. +} + +if [ ! -f "$1" ] ; then + echo "$1: No such file." 2>&1 + usage +fi + +if [ -z "$2" ] ; then + echo "$2: No letters specified." 2>&1 + usage +fi + +shift # Letters specified. +for letter in `echo $@` # For each one . . . + do + INIT_TAB_AWK="$INIT_TAB_AWK tab_search[${count_case}] = \"$letter\"; final_tab[${count_case}] = 0; " + # Pass as parameter to awk script below. + count_case=`expr $count_case + 1` +done + +# DEBUG: +# echo $INIT_TAB_AWK; + +cat $FILE_PARSE | +# Pipe the target file to 'awk.' + +#-------------------------------------------------------------------------------- +awk -v tab_search=0 -v final_tab=0 -v tab=0 -v nb_letter=0 -v chara=0 -v chara2=0 \ +"BEGIN { $INIT_TAB_AWK } \ +{ split(\$0, tab, \"\"); \ +for (chara in tab) \ +{ for (chara2 in tab_search) \ +{ if (tab_search[chara2] == tab[chara]) { final_tab[chara2]++ } } } } \ +END { for (chara in final_tab) \ +{ print tab_search[chara] \" => \" final_tab[chara] } }" +#-------------------------------------------------------------------------------- +# Nothing all that complicated, just . . . +#+ for-loops, if-tests, and a couple of specialized functions. + +exit $? diff --git a/LDP/guide/docbook/abs-guide/protect_literal.sh b/LDP/guide/docbook/abs-guide/protect_literal.sh new file mode 100644 index 00000000..23f1c1b1 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/protect_literal.sh @@ -0,0 +1,148 @@ +#! /bin/bash +# protect_literal.sh + +# set -vx + +:<<-'_Protect_Literal_String_Doc' + + Copyright (c) Michael S. Zick, 2003; All Rights Reserved + License: Unrestricted reuse in any form, for any purpose. + Warranty: None + Revision: $ID$ + + Documentation redirected to the Bash no-operation. + Bash will '/dev/null' this block when the script is first read. + (Uncomment the above set command to see this action.) + + Remove the first (Sha-Bang) line when sourcing this as a library + procedure. Also comment out the example use code in the two + places where shown. + + + Usage: + _protect_literal_str 'Whatever string meets your ${fancy}' + Just echos the argument to standard out, hard quotes + restored. + + $(_protect_literal_str 'Whatever string meets your ${fancy}') + as the right-hand-side of an assignment statement. + + Does: + As the right-hand-side of an assignment, preserves the + hard quotes protecting the contents of the literal during + assignment. + + Notes: + The strange names (_*) are used to avoid trampling on + the user's chosen names when this is sourced as a + library. + +_Protect_Literal_String_Doc + +# The 'for illustration' function form + +_protect_literal_str() { + +# Pick an un-used, non-printing character as local IFS. +# Not required, but shows that we are ignoring it. + local IFS=$'\x1B' # \ESC character + +# Enclose the All-Elements-Of in hard quotes during assignment. + local tmp=$'\x27'$@$'\x27' +# local tmp=$'\''$@$'\'' # Even uglier. + + local len=${#tmp} # Info only. + echo $tmp is $len long. # Output AND information. +} + +# This is the short-named version. +_pls() { + local IFS=$'x1B' # \ESC character (not required) + echo $'\x27'$@$'\x27' # Hard quoted parameter glob +} + +# :<<-'_Protect_Literal_String_Test' +# # # Remove the above "# " to disable this code. # # # + +# See how that looks when printed. +echo +echo "- - Test One - -" +_protect_literal_str 'Hello $user' +_protect_literal_str 'Hello "${username}"' +echo + +# Which yields: +# - - Test One - - +# 'Hello $user' is 13 long. +# 'Hello "${username}"' is 21 long. + +# Looks as expected, but why all of the trouble? +# The difference is hidden inside the Bash internal order +#+ of operations. +# Which shows when you use it on the RHS of an assignment. + +# Declare an array for test values. +declare -a arrayZ + +# Assign elements with various types of quotes and escapes. +arrayZ=( zero "$(_pls 'Hello ${Me}')" 'Hello ${You}' "\'Pass: ${pw}\'" ) + +# Now list that array and see what is there. +echo "- - Test Two - -" +for (( i=0 ; i<${#arrayZ[*]} ; i++ )) +do + echo Element $i: ${arrayZ[$i]} is: ${#arrayZ[$i]} long. +done +echo + +# Which yields: +# - - Test Two - - +# Element 0: zero is: 4 long. # Our marker element +# Element 1: 'Hello ${Me}' is: 13 long. # Our "$(_pls '...' )" +# Element 2: Hello ${You} is: 12 long. # Quotes are missing +# Element 3: \'Pass: \' is: 10 long. # ${pw} expanded to nothing + +# Now make an assignment with that result. +declare -a array2=( ${arrayZ[@]} ) + +# And print what happened. +echo "- - Test Three - -" +for (( i=0 ; i<${#array2[*]} ; i++ )) +do + echo Element $i: ${array2[$i]} is: ${#array2[$i]} long. +done +echo + +# Which yields: +# - - Test Three - - +# Element 0: zero is: 4 long. # Our marker element. +# Element 1: Hello ${Me} is: 11 long. # Intended result. +# Element 2: Hello is: 5 long. # ${You} expanded to nothing. +# Element 3: 'Pass: is: 6 long. # Split on the whitespace. +# Element 4: ' is: 1 long. # The end quote is here now. + +# Our Element 1 has had its leading and trailing hard quotes stripped. +# Although not shown, leading and trailing whitespace is also stripped. +# Now that the string contents are set, Bash will always, internally, +#+ hard quote the contents as required during its operations. + +# Why? +# Considering our "$(_pls 'Hello ${Me}')" construction: +# " ... " -> Expansion required, strip the quotes. +# $( ... ) -> Replace with the result of..., strip this. +# _pls ' ... ' -> called with literal arguments, strip the quotes. +# The result returned includes hard quotes; BUT the above processing +#+ has already been done, so they become part of the value assigned. +# +# Similarly, during further usage of the string variable, the ${Me} +#+ is part of the contents (result) and survives any operations +# (Until explicitly told to evaluate the string). + +# Hint: See what happens when the hard quotes ($'\x27') are replaced +#+ with soft quotes ($'\x22') in the above procedures. +# Interesting also is to remove the addition of any quoting. + +# _Protect_Literal_String_Test +# # # Remove the above "# " to disable this code. # # # + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/random-between.sh b/LDP/guide/docbook/abs-guide/random-between.sh new file mode 100644 index 00000000..9bfff71e --- /dev/null +++ b/LDP/guide/docbook/abs-guide/random-between.sh @@ -0,0 +1,174 @@ +#!/bin/bash +# random-between.sh +# Random number between two specified values. +# Script by Bill Gradwohl, with minor modifications by the document author. +# Used with permission. + + +randomBetween() { + # Generates a positive or negative random number + #+ between $min and $max + #+ and divisible by $divisibleBy. + # Gives a "reasonably random" distribution of return values. + # + # Bill Gradwohl - Oct 1, 2003 + + syntax() { + # Function embedded within function. + echo + echo "Syntax: randomBetween [min] [max] [multiple]" + echo + echo "Expects up to 3 passed parameters, but all are completely optional." + echo "min is the minimum value" + echo "max is the maximum value" + echo "multiple specifies that the answer must be a multiple of this value." + echo " i.e. answer must be evenly divisible by this number." + echo + echo "If any value is missing, defaults area supplied as: 0 32767 1" + echo "Successful completion returns 0, unsuccessful completion returns" + echo "function syntax and 1." + echo "The answer is returned in the global variable randomBetweenAnswer" + echo "Negative values for any passed parameter are handled correctly." + } + + local min=${1:-0} + local max=${2:-32767} + local divisibleBy=${3:-1} + # Default values assigned, in case parameters not passed to function. + + local x + local spread + + # Let's make sure the divisibleBy value is positive. + [ ${divisibleBy} -lt 0 ] && divisibleBy=$((0-divisibleBy)) + + # Sanity check. + if [ $# -gt 3 -o ${divisibleBy} -eq 0 -o ${min} -eq ${max} ]; then + syntax + return 1 + fi + + # See if the min and max are reversed. + if [ ${min} -gt ${max} ]; then + # Swap them. + x=${min} + min=${max} + max=${x} + fi + + # If min is itself not evenly divisible by $divisibleBy, + #+ then fix the min to be within range. + if [ $((min/divisibleBy*divisibleBy)) -ne ${min} ]; then + if [ ${min} -lt 0 ]; then + min=$((min/divisibleBy*divisibleBy)) + else + min=$((((min/divisibleBy)+1)*divisibleBy)) + fi + fi + + # If max is itself not evenly divisible by $divisibleBy, + #+ then fix the max to be within range. + if [ $((max/divisibleBy*divisibleBy)) -ne ${max} ]; then + if [ ${max} -lt 0 ]; then + max=$((((max/divisibleBy)-1)*divisibleBy)) + else + max=$((max/divisibleBy*divisibleBy)) + fi + fi + + # --------------------------------------------------------------------- + # Now do the real work. + + # Note that to get a proper distribution for the end points, the + #+ range of random values has to be allowed to go between 0 and + #+ abs(max-min)+divisibleBy, not just abs(max-min)+1. + + # The slight increase will produce the proper distribution for the + #+ end points. + + # Changing the formula to use abs(max-min)+1 will still produce + #+ correct answers, but the randomness of those answers is faulty in + #+ that the number of times the end points ($min and $max) are returned + #+ is considerably lower than when the correct formula is used. + # --------------------------------------------------------------------- + + spread=$((max-min)) + [ ${spread} -lt 0 ] && spread=$((0-spread)) + let spread+=divisibleBy + randomBetweenAnswer=$(((RANDOM%spread)/divisibleBy*divisibleBy+min)) + + return 0 +} + +# Let's test the function. +min=-14 +max=20 +divisibleBy=3 + + +# Generate an array of expected answers and check to make sure we get +#+ at least one of each answer if we loop long enough. + +declare -a answer +minimum=${min} +maximum=${max} + if [ $((minimum/divisibleBy*divisibleBy)) -ne ${minimum} ]; then + if [ ${minimum} -lt 0 ]; then + minimum=$((minimum/divisibleBy*divisibleBy)) + else + minimum=$((((minimum/divisibleBy)+1)*divisibleBy)) + fi + fi + + + # If max is itself not evenly divisible by $divisibleBy, + #+ then fix the max to be within range. + + if [ $((maximum/divisibleBy*divisibleBy)) -ne ${maximum} ]; then + if [ ${maximum} -lt 0 ]; then + maximum=$((((maximum/divisibleBy)-1)*divisibleBy)) + else + maximum=$((maximum/divisibleBy*divisibleBy)) + fi + fi + + +# We need to generate only positive array subscripts, +#+ so we need a displacement that that will guarantee +#+ positive results. + +displacement=$((0-minimum)) +for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do + answer[i+displacement]=0 +done + + +# Now loop a large number of times to see what we get. +loopIt=1000 # The script author suggests 100000, + #+ but that takes a good long while. + +for ((i=0; i<${loopIt}; ++i)); do + + # Note that we are specifying min and max in reversed order here to + #+ make the function correct for this case. + + randomBetween ${max} ${min} ${divisibleBy} + + # Report an error if an answer is unexpected. + [ ${randomBetweenAnswer} -lt ${min} -o ${randomBetweenAnswer} -gt ${max} ] && echo MIN or MAX error - ${randomBetweenAnswer}! + [ $((randomBetweenAnswer%${divisibleBy})) -ne 0 ] && echo DIVISIBLE BY error - ${randomBetweenAnswer}! + + # Store the answer away statistically. + answer[randomBetweenAnswer+displacement]=$((answer[randomBetweenAnswer+displacement]+1)) +done + + + +# Let's check the results + +for ((i=${minimum}; i<=${maximum}; i+=divisibleBy)); do + [ ${answer[i+displacement]} -eq 0 ] && echo "We never got an answer of $i." || echo "${i} occurred ${answer[i+displacement]} times." +done + + +exit 0 diff --git a/LDP/guide/docbook/abs-guide/readpipe.sh b/LDP/guide/docbook/abs-guide/readpipe.sh new file mode 100644 index 00000000..d2e41429 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/readpipe.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# readpipe.sh +# This example contributed by Bjon Eriksson. + +last="(null)" +cat $0 | +while read line +do + echo "{$line}" + last=$line +done +printf "\nAll done, last:$last\n" + +exit 0 # End of code. + # (Partial) output of script follows. + # The 'echo' supplies extra brackets. + +############################################# + +./readpipe.sh + +{#!/bin/sh} +{last="(null)"} +{cat $0 |} +{while read line} +{do} +{echo "{$line}"} +{last=$line} +{done} +{printf "nAll done, last:$lastn"} + + +All done, last:(null) + +The variable (last) is set within the subshell but unset outside. diff --git a/LDP/guide/docbook/abs-guide/unprotect_literal.sh b/LDP/guide/docbook/abs-guide/unprotect_literal.sh new file mode 100644 index 00000000..79fb6fc7 --- /dev/null +++ b/LDP/guide/docbook/abs-guide/unprotect_literal.sh @@ -0,0 +1,123 @@ +#! /bin/bash +# unprotect_literal.sh + +# set -vx + +:<<-'_UnProtect_Literal_String_Doc' + + Copyright (c) Michael S. Zick, 2003; All Rights Reserved + License: Unrestricted reuse in any form, for any purpose. + Warranty: None + Revision: $ID$ + + Documentation redirected to the Bash no-operation. Bash will + '/dev/null' this block when the script is first read. + (Uncomment the above set command to see this action.) + + Remove the first (Sha-Bang) line when sourcing this as a library + procedure. Also comment out the example use code in the two + places where shown. + + + Usage: + Complement of the "$(_pls 'Literal String')" function. + (See the protect_literal.sh example.) + + StringVar=$(_upls ProtectedSringVariable) + + Does: + When used on the right-hand-side of an assignment statement; + makes the substitions embedded in the protected string. + + Notes: + The strange names (_*) are used to avoid trampling on + the user's chosen names when this is sourced as a + library. + + +_UnProtect_Literal_String_Doc + +_upls() { + local IFS=$'x1B' # \ESC character (not required) + eval echo $@ # Substitution on the glob. +} + +# :<<-'_UnProtect_Literal_String_Test' +# # # Remove the above "# " to disable this code. # # # + + +_pls() { + local IFS=$'x1B' # \ESC character (not required) + echo $'\x27'$@$'\x27' # Hard quoted parameter glob +} + +# Declare an array for test values. +declare -a arrayZ + +# Assign elements with various types of quotes and escapes. +arrayZ=( zero "$(_pls 'Hello ${Me}')" 'Hello ${You}' "\'Pass: ${pw}\'" ) + +# Now make an assignment with that result. +declare -a array2=( ${arrayZ[@]} ) + +# Which yielded: +# - - Test Three - - +# Element 0: zero is: 4 long # Our marker element. +# Element 1: Hello ${Me} is: 11 long # Intended result. +# Element 2: Hello is: 5 long # ${You} expanded to nothing. +# Element 3: 'Pass: is: 6 long # Split on the whitespace. +# Element 4: ' is: 1 long # The end quote is here now. + +# set -vx + +# Initialize 'Me' to something for the embedded ${Me} substitution. +# This needs to be done ONLY just prior to evaluating the +#+ protected string. +# (This is why it was protected to begin with.) + +Me="to the array guy." + +# Set a string variable destination to the result. +newVar=$(_upls ${array2[1]}) + +# Show what the contents are. +echo $newVar + +# Do we really need a function to do this? +newerVar=$(eval echo ${array2[1]}) +echo $newerVar + +# I guess not, but the _upls function gives us a place to hang +#+ the documentation on. +# This helps when we forget what a # construction like: +#+ $(eval echo ... ) means. + +# What if Me isn't set when the protected string is evaluated? +unset Me +newestVar=$(_upls ${array2[1]}) +echo $newestVar + +# Just gone, no hints, no runs, no errors. + +# Why in the world? +# Setting the contents of a string variable containing character +#+ sequences that have a meaning to Bash is a general problem in +#+ script programming. +# +# This problem is now solved in eight lines of code +#+ (and four pages of description). + +# Where is all this going? +# Dynamic content Web pages as an array of Bash strings. +# Content set per request by a Bash 'eval' command +#+ on the stored page template. +# Not intended to replace PHP, just an interesting thing to do. +### +# Don't have a webserver application? +# No problem, check the example directory of the Bash source; +#+ there is a Bash script for that also. + +# _UnProtect_Literal_String_Test +# # # Remove the above "# " to disable this code. # # # + +exit 0