46

I'm sure it is relatively simple, I just don't know how to do it.

#!/usr/bin/ksh
set `iostat`
myvar=6

I want to something like echo ${$myvar} which i want interpreted as ${$myvar} -> ${6} -> value

mirabilos
  • 1,733

4 Answers4

46

You can do this with eval, built-in to many fine shells, including ksh:

#!/usr/bin/ksh
set $(iostat)
myvar=6
eval "echo \${$myvar}"

The trick is to double-quote the string you feed to eval so that $myvar gets substituted with "6", and to backslash the outer dollar-sign, so that eval gets a string "$6".

I got "%user" for the output, but I tried it on a multi-processor RHEL machine.

SebMa
  • 2,149
  • 6
    You are officially the Supreme Exalted Grand Master of the week b/c that even works on the unfathomably awful ksh (really pdksh) in OpenBSD 5.4. If you want to set var vv to the value of the var whose name is in the var vn, just do vv=$( eval "echo \$$vn" ). Thanks a ton! – execNext Apr 05 '14 at 00:07
38

Indirect variable reference

Modern advanced shells have a method to reference the value of a variable whose name is stored in another variable. Unfortunately the method differs between ksh, bash and zsh.

In mksh ≥R39b, you can make myvar a nameref:

typeset -n myvar=6
echo "$myvar"

This doesn't work in ATT ksh93 because it doesn't support namerefs to positional parameters. In the case where you have a variable containing a variable name, you can use this method.

foo=bar
typeset -n myvar=foo
echo "$myvar"  # prints bar

In bash ≥2.0, you can write

echo "${!myvar}"

In zsh, you can write

echo ${(P)myvar}

In older shells, including ksh88 and pdksh, your only recourse when you have a variable containing another variable name and want to use the value of this variable eval, as explained by Bruce Ediger. This solution works in any Bourne/POSIX shell.

eval "value=\${$myvar}"
echo "$value"

Using an array

This is the best method here: it's simpler and more portable.

For your use case, in any shell with arrays (all ksh variants, bash ≥2.0, zsh), you can assign to an array variable and take the element you wish. Beware that ksh and bash arrays start numbering at 0, but zsh starts at 1 unless you issue setopt ksh_arrays or emulate ksh.

set -A iostat -- $(iostat)
echo "${iostat[5]}"

If you want to copy the positional parameters to an array variable a:

set -A a -- "$@"

In ksh93, mksh ≥R39b, bash ≥2.0 and zsh, you can use the array assignment syntax:

iostat=($(iostat))
echo "${iostat[5]}"
1

As indicated by Gilles (who provided the bash part of the answer), also not invalidating Bruce Ediger’s (on how to do it portably with eval), here’s how to do it with nameref in recent mksh (and AT&T ksh93, except – as @Gilles commented – namerefs cannot refer to positional parameters in AT&T ksh, only to named parameters):

#!/bin/mksh
set -- $(iostat)
nameref myvar=6
echo $myvar

Added the -- after set for improved resistence, too.

mirabilos
  • 1,733
0

Another use of arrays

Haven't used either ksh or any variant for some time, so I'm not sure if ksh(or bash) has a similar capability. My primary shell is zsh. I use arrays when handling output from commands like iostat because they produce multiple lines, and not all lines are the same format/length.

#! /bin/zsh
IOStatOutput=("${(@f)$(iostat)}") # Produces one element per line

The above also bypasses the use of positional parameters. Now, if you want to generate, say, an array of devices:

for Element in {7..${#IOStatOutput}} # Devices listed in elements 7 thru the last
do
    DevList+=( ${${=IOStatOutput[Element]}[1]} )
done

I find smaller chunks much easier to handle. You may or may not need to use indirect variable reference, depending on your code. Knowing how it works is still a good thing to know. I use it myself.

Friartek
  • 411