LDP/LDP/guide/docbook/abs-guide/basics-reviewed.bash

1041 lines
31 KiB
Bash

#!/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")
# Fixes and updates (04/08) by Cliff Bamford.
# 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 436.
# The current Bash maintainer, Chet Ramey, has fixed the items noted
#+ for later versions 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 may 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 builtin.
echo $VarSomething
# The printf command is a builtin.
# 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 may 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
echo "========================================================="
# Lines 202 - 334 supplied by Cliff Bamford. (Thanks!)
# Demo --- Interaction with Arrays, quoting, IFS, echo, * and @ ---
#+ all affect how things work
ArrayVar[0]='zero' # 0 normal
ArrayVar[1]=one # 1 unquoted literal
ArrayVar[2]='two' # 2 normal
ArrayVar[3]='three' # 3 normal
ArrayVar[4]='I am four' # 4 normal with spaces
ArrayVar[5]='five' # 5 normal
unset ArrayVar[6] # 6 undefined
ArrayValue[7]='seven' # 7 normal
ArrayValue[8]='' # 8 defined but empty
ArrayValue[9]='nine' # 9 normal
echo '--- Here is the array we are using for this test'
echo
echo "ArrayVar[0]='zero' # 0 normal"
echo "ArrayVar[1]=one # 1 unquoted literal"
echo "ArrayVar[2]='two' # 2 normal"
echo "ArrayVar[3]='three' # 3 normal"
echo "ArrayVar[4]='I am four' # 4 normal with spaces"
echo "ArrayVar[5]='five' # 5 normal"
echo "unset ArrayVar[6] # 6 undefined"
echo "ArrayValue[7]='seven' # 7 normal"
echo "ArrayValue[8]='' # 8 defined but empty"
echo "ArrayValue[9]='nine' # 9 normal"
echo
echo
echo '---Case0: No double-quotes, Default IFS of space,tab,newline ---'
IFS=$'\x20'$'\x09'$'\x0A' # In exactly this order.
echo 'Here is: printf %q {${ArrayVar[*]}'
printf %q ${ArrayVar[*]}
echo
echo 'Here is: printf %q {${ArrayVar[@]}'
printf %q ${ArrayVar[@]}
echo
echo 'Here is: echo ${ArrayVar[*]}'
echo ${ArrayVar[@]}
echo 'Here is: echo {${ArrayVar[@]}'
echo ${ArrayVar[@]}
echo
echo '---Case1: Within double-quotes - Default IFS of space-tab-
newline ---'
IFS=$'\x20'$'\x09'$'\x0A' # These three bytes,
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case2: Within double-quotes - IFS is q'
IFS='q'
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case3: Within double-quotes - IFS is ^'
IFS='^'
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case4: Within double-quotes - IFS is ^ followed by
space,tab,newline'
IFS=$'^'$'\x20'$'\x09'$'\x0A' # ^ + space tab newline
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case6: Within double-quotes - IFS set and empty '
IFS=''
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---Case7: Within double-quotes - IFS is unset'
unset IFS
echo 'Here is: printf %q "{${ArrayVar[*]}"'
printf %q "${ArrayVar[*]}"
echo
echo 'Here is: printf %q "{${ArrayVar[@]}"'
printf %q "${ArrayVar[@]}"
echo
echo 'Here is: echo "${ArrayVar[*]}"'
echo "${ArrayVar[@]}"
echo 'Here is: echo "{${ArrayVar[@]}"'
echo "${ArrayVar[@]}"
echo
echo '---End of Cases---'
echo "========================================================="; echo
# 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 436: 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.
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.
# echo ${sparseZ[@]#abc} # Version-2.05b core dumps.
# Has since been fixed by Chet Ramey.
# 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
# echo ${sparseZ[@]##a*c} # Version-2.05b core dumps.
# Has since been fixed by Chet Ramey.
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.
# echo ${sparseZ[@]%abc} # Version-2.05b core dumps.
# Has since been fixed by Chet Ramey.
# 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
# echo ${sparseZ[@]%%b*c} # Version-2.05b core dumps.
# Has since been fixed by Chet Ramey.
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