1

I am new to Unix shell scripting My code is as follows:

  i=0
for var in 'a' 'b' 'c' 'd' 'e'
do
  content[i]=var
  ((i=`expr i+1`))
done
a=10
b=50
c=40
d=90
e=100
Now I wanted to print the local variables contents using array into the function

print_array
{
echo "${content[0]}"
echo "${content[1]}"
echo "${content[2]}"
echo "${content[3]}"
echo "${content[4]}"
} 

what I wanted to do that array contents are {a,b,c,d,e} and there is a local variables named a,b,c,d,e with some assigned values I wanted to print those local variables through the use of array since the name of array element and local variable element is same so I tried to call by reference by trying multiple combinations of $ and array_names but not working

Aman
  • 1,151
  • 2
  • 11
  • 17

3 Answers3

1

Try

#i=0
for var in 'a' 'b' 'c' 'd' 'e'
do
  content[${#content[*]}]=$var  # or content[i++]=$var
done
# or just content=( 'a' 'b' 'c' 'd' 'e' )
a=10
b=50
c=40
d=90
e=100
for i in "${#content[@]}"
do
  echo ${!i}
done
Costas
  • 14,806
  • 20
  • 36
1

So I'll define an indirect printing function first...

_print() while [ "$#" -ne 0 ]
         do    printf '$%s = %d\n' \
                       "$1" "$(($1))"
               shift
         done

Next I'll setup the array and increment...

arr=( a b c d e ); i=0
for var in "${arr[@]}"
do  : "$(($var=(i+=10)))"
done

So now the value of $a is 10 and $b 20 and so on. Last there remains only to print...

_print "${arr[@]}"

...which prints to stdout...

$a = 10
$b = 20
$c = 30
$d = 40
$e = 50

All of this works because of the way the shell handles $((math)) expansions - they're basically evals. In an arithmetic expansion the shell first expands any other valid shell expansion before attempting to do the math - making the actual math its last order of business.

This means that if you do:

i=a a=10; echo "$(($i))" 

The printed result is 10 because the shell expands the value of $i to get a then evaluates that result as an integer reference.

The above will work in any POSIX-compliant shell.

This means I could possibly also have done...

i=10
for var in a b c d e
do  arr[($var=i*${#arr[@]}+i)/i]=$var
done

...to handle array assignment, index evaluation, and $var integer assignment at once because the [index] brackets of a named shell array - in a shell which supports them - are treated identically to the $((expansion)) parens of a math expression.

Running the above code wrapped in ksh -xc command prints the following debug output to standard error:

+ arr[1]=a
+ arr[2]=b
+ arr[3]=c
+ arr[4]=d
+ arr[5]=e

From there I can just do:

echo "$((${arr[1]}))"
echo "$((a))"

...to print...

10
10

...because they evaluate to the same thing in shells which support named arrays. However, in shells which don't...

echo 'arr=(a b c d e)' | bash -x
+ arr=(a b c d e)     #bash traces the successful command to stderr
echo 'arr=(a b c d e)' | sh -x
sh: 1: Syntax error: "(" unexpected   #sh traces something else

So in the _print() function I just shift over the positional parameters (which represents a truly portable shell "$@"array) while there are any at all and printf...

  1. First a $ dollar sign.
  2. Then the %string value stored in my $1st positional parameter.
  3. Then an = equals sign.
  4. And last the value stored in the value stored in my $(($1)) first positional parameter.

As the function shifts its arguments away the first positional parameter is constantly being replaced with the next until the $# count of positionals equals 0 and the function returns.

Before running the function when I initialize the array and its constituent indirection variables that works like this:

  • for var in "${arr[@]}"

    • The shell will expand [@] to a list of arguments and [*] to a single concatenation of that list. If the expansion is not quoted it might also expand [*] out to a list as well - depending on whether or not there is a value for $IFS when it is done and how set -filename expansion is currently configured - but if it does so it likely does not do it the way you intend.
  • : "$(($var=(i+=10)))"

    • Each value in ${arr[@]} is assigned to the value of $var in turn. $(($var=(i+=10))) is then expanded first for the value in $var like $((a=(i+=10))) and last of all the math is done - which first increments $i by 10 and next assigns the value of $i to $a.
mikeserv
  • 57,448
  • 9
  • 113
  • 229
  • @mikerserv It is giving error 0403-057 Syntax error at line 6 : `(' is not expected. means at this line arr=( a b c d e ); i=0 then I have changed it to arr= 'a' 'b' 'c' 'd' 'e' then error message a: not found is coming please guide me to solve it thanks a lot for your descriptive answer :) – Aman Apr 07 '15 at 11:13
  • @Aman - `a: not found` sounds like you're trying to run a command. Which version of `ksh`? – mikeserv Apr 07 '15 at 11:49
  • I am running on bash 4.2 – Aman Apr 07 '15 at 13:28
  • @Aman - well, then you must have entered something in wrong - everything up there works in `bash` - at least it does on my system. Have you tried it since edited it? It shouldn't make a difference though... – mikeserv Apr 07 '15 at 14:15
  • I am going to retest it. – Aman Apr 07 '15 at 14:19
  • -bash-4.2$ cat >lastexp.ksh _print() while [ "$#" -ne 0 ] do printf '$%s = %d\n' \ "$1" "$(($1))" shift done arr=( a b c d e ); i=0 for var in "${arr[@]}" do : "$(($var=(i+=10)))" done _print "${arr[@]}" ^C -bash-4.2$ sh lastexp.ksh lastexp.ksh[6]: 0403-057 Syntax error at line 6 : `(' is not expected. – Aman Apr 07 '15 at 14:21
  • @Aman - you're trying to enter it all in on one line? And what is *lastexp.ksh*? You can't do that. Where there are line breaks in the command examples you need line breaks - either that or semi-colons. If you want to do it at the terminal prompt you can, but enter it in by line as it is presented. I guess I could make a copy/paste version for you if you want. A tip, by the way - to enter a `\n`ewline at your prompt you can probably use CTRL+V then CTRL+J. Your error though is due to the function and array definition running together - it would need to be `done; arr=(` at that point, – mikeserv Apr 07 '15 at 14:27
  • @Aman - here is that command all on a single line, if you really want it that way: `_print() while [ "$#" -ne 0 ]; do printf '$%s = %d\n' "$1" "$(($1))"; shift; done;arr=( a b c d e ); i=0;for var in "${arr[@]}";do : "$(($var=(i+=10)))";done;_print "${arr[@]}";` Maybe also try: `i=25; for var in a b c d e; do arr[($var=i*${#arr[@]}+i)/i]=$var; done` and afterward running `_print "${arr[@]}"` again - the results are the same, except that you can put different numbers in `i` and it will increment automatically. So `i=3` will increment each assigned `$var` by three as they are assigned. – mikeserv Apr 07 '15 at 14:42
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/22632/discussion-between-aman-and-mikeserv). – Aman Apr 07 '15 at 15:31
  • again not working -bash-4.2$ cat > testksh.ksh _print() while [ "$#" -ne 0 ]; do printf '$%s = %d\n' "$1" "$(($1))"; shift; done;arr=( a b c d e ); i=0;for var in "${arr[@]}";do : "$(($var=(i+=10)))";done;_print "${arr[@]}"; ^C -bash-4.2$ sh testksh.ksh testksh.ksh: 0403-057 Syntax error at line 1 : `(' is not expected. – Aman Apr 07 '15 at 15:33
  • @Aman Oh, yeah, of course not. You're invoking `sh` - not `bash`. While it's possible the two share the same executable *(which is true of some systems)* `bash` doesn't do arrays when invoked as `sh` because it goes into POSIX conformance mode. You won't have any luck with this at all that way. And what is this *testksh.ksh* stuff? Anyway, there *are* arrays *(of my own preferred)* in POSIX-mode - but those are the positional arrays which are created one per function - *(I use one above when working with `$1`)* but that's a different thing than named arrays. There's no - `arr[index]=` either. – mikeserv Apr 07 '15 at 15:39
  • @Aman - see the edit I made here and maybe see [here](http://unix.stackexchange.com/a/179754/52934). – mikeserv Apr 07 '15 at 16:02
1

If I understand you correctly you want an indirection in variable's access with, e.g., bash. With this code:

content=( a b c d e )

a=10
b=50
c=40
d=90
e=100

echo "${!content[0]}"
echo "${!content[1]}"
echo "${!content[2]}"
echo "${!content[3]}"
echo "${!content[4]}"

You will get this result:

10
50
40
90
100

The key here is bash's specific variable expansion access method "${! ... }".

Janis
  • 14,014
  • 3
  • 25
  • 42
  • I have bash 4.2 which does not support echo "${!content[4]}" mentioned process substitution please guide me :) – Aman Apr 07 '15 at 17:43
  • @Aman; the construct that I proposed is certainly running even in old `bash` versions. In **every** proposed solution you claim you are running a `bash` and getting errors. So I have to say I have my doubts that what you say is true! - Try two things: `bash --version`, and (instead of calling your script as `./yourscript`) call your script **explicitly** with `bash yourscript`. – Janis Apr 08 '15 at 06:31
  • @Aman; I just see in an analysis of another poster's answer that you are running `sh` and not `bash`. So use the form I proposed to call your script, `bash yourscript`, and everything should work fine. – Janis Apr 08 '15 at 06:38