LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   [bash] dynamic variables - assign value containing spaces (https://www.linuxquestions.org/questions/programming-9/%5Bbash%5D-dynamic-variables-assign-value-containing-spaces-4175444271/)

hashbang#! 01-05-2013 11:42 AM

[bash] dynamic variables - assign value containing spaces
 
I have a lot of repetitive code here and I tried to replace this piece of code with dynamic variables using eval:

Code:

[[ ! -z "$bg" ]] && bg_canvas=$(canvas $bg_colour)
[[ ! -z "$fg" ]] && fg_canvas=$(canvas $fg_colour)
...

I would like to achieve something like this:

Code:

for t in fg bg; do
    eval [[ ! -z "'$'${t}" ]] && ${t}_canvas=$(canvas '$'${t}_colour)
done

I ran into two problems:
  1. I didn't know how to test the value of dynamic variable '$'${t}
  2. The output of $(canvas $fg_colour) contains spaces, e.g. "#ff0 #00a", which leads to error bash: #00a: command not found

Any help would be very much appreciated.

grail 01-05-2013 12:19 PM

Generally there are very few reasons to use dynamic variable names. My suggestion would be to use associative arrays using the appropriate names, bg & fg, and test and set as needed.
This will also negate the issue you are having with spaces as you simply quote the whole variable.

And of course the use of eval should always be kept to a minimum or avoided where possible.

hashbang#! 01-05-2013 05:32 PM

Thank you for your response, grail.

On this occasion I would find it more convenient to use dynamic variables as I have already used this technique elsewhere in the script.

My main stumbling block is 2) - assigning values with spaces to dynamic variables. Is there any way round the problem?
For 1) I have a work-around.

grail 01-06-2013 12:23 AM

Quote:

On this occasion I would find it more convenient to use dynamic variables as I have already used this technique elsewhere in the script.
As the old saying goes, continuing with a bad idea because you started with it is hardly a reason to continue.

That being said, the only way I know of nullifying the space issue is either:

1. use double quotes where necessary

2. assuming the above does not work, replace all spaces with something like an underscore and then reverse the process when needing the space version

Generally the idea for me would be, if you need to use option 2 then the overhead associated would mean you need to rethink the design choice.

If David H sees this thread he may have other ideas.

millgates 01-06-2013 06:14 AM

First of all, don't use eval.
Also, and I can't stress this enough, you shouldn't use eval.
Last, but not least, DON'T USE EVAL!

This is exactly what the associative arrays are for:

Code:

#!/bin/bash

declare -A array
array[bg_color]="foo  bar"
array[fg_color]="baz"

for i in bg fg; do
    echo "i: ${array["${i}_color"]}"
done

Another way of indirection, if you really don't want to use arrays:
Code:

#!/bin/bash

bg_color="foo  bar"
fg_color=baz
for i in bg fg; do
    var="${i}_color"
    echo "$var: ${!var}"
done

If you really really really can't sleep well without using eval, (did I mention you shouldn't?), you have to quote what you don't want to be word-split. Eval will make the line be processed twice, so you may need to use two layers of quotes or escape them:

Code:

#!/bin/bash

bg_color="foo  bar"
fg_color=baz
for i in bg fg; do
    eval echo "$i: \"\$${i}_color\""
done


rknichols 01-06-2013 12:51 PM

Quote:

Originally Posted by hashbang#! (Post 4863515)
I would like to achieve something like this:

Code:

for t in fg bg; do
    eval [[ ! -z "'$'${t}" ]] && ${t}_canvas=$(canvas '$'${t}_colour)
done


This should work, using an intermediate variable for the parameter name, '!' for indirection, and "declare" to allow expansion of the LHS of the assignment:
Code:

for T in fg bg; do
    Param=${t}_colour
    [[ ! -z "${!t}" ]] && declare ${t}_canvas="$(canvas ${!Param})"
done


hashbang#! 01-06-2013 01:01 PM

Quote:

Originally Posted by millgates (Post 4863840)
First of all, don't use eval.
Also, and I can't stress this enough, you shouldn't use eval.
Last, but not least, DON'T USE EVAL!

You made me smile on this miserable rainy Sunday. ;)
But neither of you mentioned what's so evil about eval.

Quote:

Originally Posted by millgates (Post 4863840)
Another way of indirection, if you really don't want to use arrays:
[...]

I like the ${!var} style indirection a lot but it doesn't seem to be possible to assign a value to a dynamically referenced variable:
Code:

${!var}=blue

hashbang#! 01-06-2013 01:07 PM

rknichols, our posts crossed. I am in the process of testing your suggestion.

hashbang#! 01-06-2013 01:19 PM

Quote:

Originally Posted by rknichols (Post 4864068)
This should work, using an intermediate variable for the parameter name, '!' for indirection, and "declare" to allow expansion of the LHS of the assignment:
Code:

for T in fg bg; do
    Param=${t}_colour
    [[ ! -z "${!t}" ]] && declare ${t}_canvas="$(canvas ${!Param})"
done


That is ingenious!

colucix 01-06-2013 02:34 PM

Just an aside note: instead of
Code:

! -z
you can use
Code:

-n
which is the same but more concise.

colucix 01-06-2013 02:37 PM

Quote:

Originally Posted by hashbang#! (Post 4864072)
But neither of you mentioned what's so evil about eval.

Here is a useful link: http://mywiki.wooledge.org/BashFAQ/048 and here another article about associative arrays and indirect reference: http://mywiki.wooledge.org/BashFAQ/006.

hashbang#! 01-06-2013 04:13 PM

colucix - thanks for the useful links


All times are GMT -5. The time now is 08:00 AM.