Differences between revisions 12 and 53 (spanning 41 versions)
Revision 12 as of 2009-05-15 02:22:35
Size: 6250
Editor: localhost
Comment:
Revision 53 as of 2016-04-25 09:34:05
Size: 237
Editor: TimmyStroh
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
<<Anchor(faq6)>>
== How can I use variable variables (indirect variables, pointers, references) or associative arrays? ==
There are two halves to this: evaluating variables, and assigning values. We'll take each half separately:

=== Evaluating indirect/reference variables ===
[[BASH]] allows you to expand a parameter ''indirectly'' -- that is, one variable may contain the name of another variable:
 {{{
 # Bash
 realvariable=contents
 ref=realvariable
 echo "${!ref}" # prints the contents of the real variable
 }}}

KornShell (ksh93) has a completely different, more powerful syntax -- the `nameref` command (also known as `typeset -n`):
 {{{
 # ksh93
 realvariable=contents
 nameref ref=realvariable
 echo "$ref" # prints the contents of the real variable
 }}}

ksh93's `nameref` allows us to work with references to [[BashFAQ/005|arrays]], as well as regular scalar variables. For example,
 {{{
 # ksh93
 myfunc() {
   nameref ref=$1
   echo "array $1 has ${#ref[*]} elements"
 }
 realarray=(...)
 myfunc realarray
 }}}

We are not aware of any trick that can duplicate that functionality in Bash, POSIX or Bourne shells (short of using [[BashFAQ/048|eval]], which is extremely difficult to do securely).

Unfortunately, for shells other than Bash and ksh93, there is no syntax for ''evaluating'' a referenced variable. You would have to use [[BashFAQ/048|eval]], which means you would have to undergo extreme measures to sanitize your data to avoid catastrophe.

=== Assigning indirect/reference variables ===
Assigning a value "through" a reference (or pointer, or indirect variable, or whatever you want to call it -- I'm going to use "ref" from now on) is more widely possible, but the means of doing so are extremely shell-specific.

In ksh93, we can just use `nameref` again:
 {{{
 # ksh93
 nameref ref=realvariable
 ref="contents"
 # realvariable now contains the string "contents"
 }}}

In Bash, we can use {{{read}}} and Bash's ''here string'' syntax:
 {{{
 # Bash
 ref=realvariable
 read $ref <<< "contents"
 # realvariable now contains the string "contents"
 }}}

This works equally well with Bash array variables too:
 {{{
 # Bash
 aref=realarray
 read -a $aref <<< "words go into array elements"
 echo "${realarray[1]}" # prints "go"
 }}}

Another trick is to use Bash's {{{printf -v}}} (only available in [[BashFAQ/061|recent versions]]):
 {{{
 # Bash 3.1 or higher
 ref=realvariable
 printf -v $ref "contents"
 }}}

The {{{printf -v}}} trick is handy if your contents aren't a constant string, but rather, something dynamically generated. You can use all of {{{printf}}}'s formatting capabilities.

Yet another trick is Korn shell's {{{typeset}}} or Bash's {{{declare}}}. These are roughly equivalent to each other. Both of them cause a variable to become ''locally scoped'' to a function, if used inside a function; but if used outside a function, they can operate on global variables.

 {{{
 # Korn shell (all versions):
 typeset $ref="contents"

 # Bash:
 declare $ref="contents"
 }}}

The advantage of using `typeset` or `declare` over `eval` is that the right hand side of the assignment is ''not'' parsed by the shell. If you used `eval` here, you would have to sanitize/escape the entire right hand side first.

If you aren't using Bash or Korn shell, you can still do assignments to referenced variables using ''here document'' syntax:
 {{{
 # Bourne
 ref=realvariable
 read $ref <<EOF
 contents
 EOF
 }}}

Remember that, when using a here document, if the sentinel word ({{{EOF}}} in our example) is unquoted, then parameter expansions will be performed inside the body. If the sentinel is quoted, then parameter expansions are not performed. Use whichever is more convenient for your task.

=== Associative Arrays ===
Sometimes it's convenient to have associative arrays, arrays indexed by a string. Awk has associative arrays. Perl calls them "hashes", while Tcl simply calls them "arrays". [[KornShell|ksh93]] supports this kind of array:

 {{{
 # ksh93
 typeset -A homedir # Declare ksh93 associative array
 homedir[jim]=/home/jim
 homedir[silvia]=/home/silvia
 homedir[alex]=/home/alex
 
 for user in ${!homedir[@]} # Enumerate all indices (user names)
 do
     echo "Home directory of user $user is ${homedir[$user]}"
 done
 }}}

BASH version 4.0 finally supports them, though older versions do not.

 {{{
 # bash 4.0
 declare -A homedir
 homedir[jim]=/home/jim
 ... (same as the ksh93 example, other than declare vs. typeset)
 }}}

If you can't use ksh93 or bash 4.0, consider switching to awk, perl, ksh93, tcl, etc. if you need this type of data structure to solve your problem.

Before you think of using `eval` to mimic this behavior in a shell (probably by creating a set of variable names like `homedir_alex`), try to think of a simpler approach that you could use instead. If this hack still seems to be the best thing to do, have a look at the following disadvantages:

 1. It's hard to read and to maintain.
 1. The variable names must match the RegularExpression {{{^[a-zA-Z_][a-zA-Z_0-9]*}}} -- i.e., a variable name cannot contain arbitrary characters but only letters, digits, and underscores. We cannot have a variable's name contain Unix usernames, for instance -- consider a user named {{{hong-hu}}}. A dash '-' cannot be part of a variable name, so the entire attempt to make a variable named `homedir_hong-hu` is doomed from the start.
 1. Quoting is hard to get right. If content strings (not variable name) can contain whitespace characters and quotes, it's hard to quote it right to preserve it through both shell parsings. And that's just for ''constants'', known at the time you write the program.
 1. If the program handles unsanitized user input, it can be [[BashFAQ/048|VERY dangerous]]!

There is, however, no danger if the right-hand variable expansion is done on the second pass:

{{{
 var=myVar
 eval "$var=\$value"
}}}

This expands to the statment that is executed:

{{{
 myVar=$value
}}}

The contents of {{{$value}}} is not parsed by the shell, and there is no danger of unwanted side effects.
Im addicted to my hobby Archery. Seems boring? Not!<<BR>><<BR>>
I to learn German in my free time.<<BR>><<BR>>
<<BR>><<BR>>
Feel free to surf to my blog post: [[https://www.facebook.com/clashofclanshackf/|hack tool clash of clans]]

Im addicted to my hobby Archery. Seems boring? Not!

I to learn German in my free time.



Feel free to surf to my blog post: hack tool clash of clans

BashFAQ/006 (last edited 2023-04-14 06:52:11 by ormaaj)