The introduction
BASH, like many programming and scripting languages, has pointer types. The word pointer says it all, it points to something else, it references something. You can use the contents of any scalar value in BASH as reference by using the ${!varname}
syntax.
To create an actual pointer variable though you need to declare -n
it.
Once you declare a variable as pointer you can use the ${!varname}
syntax to find out where it points to
An example
#!/bin/bash # save as /tmp/bashpointer.sh targetVariable="the_target" the_target="the value being pointed at" echo "The contents of the ${targetVariable} variable is '${!targetVariable}'" declare -n aRealPointer="the_target" echo "The contents of the ${!aRealPointer} variable is '${aRealPointer}'"
$ sh /tmp/bashpointer.sh The contents of the the_target variable is 'the value being pointed at' The contents of the the_target variable is 'the value being pointed at'
A task that I come across often is the processing of configuration files. We check if a variable is defined and copy it to the actual variable used throughout the script. This job is quite tedious and error-prone for anything more than a single variable.
This can be solved easily enough though with BASH pointers.
Let's make an array where each even-numbered index contains the variable to set, and each odd-numbered index contains the variable to read:
CHECKS=( "name" "profile_name" "value" "profile_value" )
So we go through the list and check if the odd-numbered variable is set and if it is copy it to the even-numbered variable:
A first attempt
#!/bin/bash # save as /tmp/refs.sh profile_name="The name of the profile" profile_value="The value of the profile" name="___name" value="___value" CHECKS=( "name" "profile_name" "value" "profile_value" ) set - ${CHECKS[@]} typeset -n theref while [ $# -gt 0 ]; do theref=$1 [ -n "${!2}" ] && theref="$2" shift 2 done echo " Name: ${name}" echo "Value: ${value}"
$ sh /tmp/refs.sh Name: The value of the profile Value: ___value
That is unexpected! What is going on? The Value variable did not get set, and the Name variable has got the wrong value!
If we analyse this a bit more though, we can make the following observations:
- The value of the Name variable changed therefore we have successfully created a pointer to the Name variable
- The value meant for the Value variable has been put in the Name variable
- The Value variable has not changed which means we have never been able to create a pointer to it
All of these observations spell out one thing: Once set, the pointer's target has not changed
This is corroborated by the BASH manual for declare -n
:
declare [-aAfFgilnrtux] [-p] [name[=value] ...] typeset [-aAfFgilnrtux] [-p] [name[=value] ...] ... -n Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except those using or changing the -n attribute itself, are performed on the variable referenced by name's value. The nameref attribute cannot be applied to array variables.
OK, according to this we can only update the value being pointed at by removing the -n
attribute first:
Second attempt
#!/bin/bash # save as /tmp/refs2.sh profile_name="The name of the profile" profile_value="The value of the profile" name="___name" value="___value" CHECKS=( "name" "profile_name" "value" "profile_value" ) set - ${CHECKS[@]} while [ $# -gt 0 ]; do typeset -n theref="$1" [ -n "${!2}" ] && theref="$2" shift 2 typeset +n theref done echo " Name: ${name}" echo "Value: ${value}"
$ sh /tmp/refs2.sh Name: profile_name Value: profile_value
Success!!
In conclusion
There is no understating the merrits of pointers, yet the age old addage about great power and responsibility applies here too. Being responsible with BASH pointers means flipping the -n
property prior to changing its target, or, using a local variable that goes out of scope between calls
No comments:
Post a Comment