How can you echo the 'output' of echoing a variable in bash? - bash

Sorry if the title isn't very clear. So, I'm trying to echo a variable in bash after using read to get a variable ($A in this example) from a user, like so:
foobar="good"
A=bar
B="\$foo$A"
echo $B
But the output of echoing $B is "$foobar", instead of "good", which is what I want it to be. I've tried using eval $B at the end, but it just told me that "good" isn't a command. eval $(echo $B) didn't work either, it also told me that "good" isn't a command. echo $(eval $B) also told me that "good" isn't a command, and also prints an empty line.
To help clarify why I need to do this: foobar is a variable that contains the string "good", and foobaz contains "bad", and I want to let a user choose which string is echoed by typing either "bar" or "baz" while a program runs read A (in this example I just assigned A as bar to make this question more understandable).
If anyone could please tell me how I can make this work, or offer related tips, I'd appreciate it, thanks.

You're looking for indirect references:
$ foobar="hello"
$ A=bar
$ B="foo$A"
$ echo "$B" "${!B}"
foobar hello
You should take a look at the How can I use variable variables FAQ for more information about those (and associative arrays).

Related

bash not recognizing new lines in multiline variable assignment [duplicate]

In Bash (or other shells) how can I print an environment variable which has a multi-line value?
text='line1
line2'
I know a simple usual echo $text won't work out of the box.
Would some $IFS tweak help?
My current workaround is something like ruby -e 'print ENV["text"]'.
Can this be done in pure shell? I was wondering if env command would take an unresolved var name but it does not seem to.
Same solution as always.
echo "$text"
export TEST="A\nB\nC"
echo $TEST
gives output:
A\nB\nC
but:
echo -e $TEST
A
B
C
So, the answer seems to be the '-e' parameter to echo, assuming that I understand your question correctly.

Bad Substitution - Variable name inside other variable name - in Bash

I have a problem in one of my scripts, here it is simplified.
I want to name a variable using another variable in it. My script is:
#! /bin/bash
j=1
SAMPLE${j}_CHIP=5
echo ${SAMPLE${j}_CHIP}
This script echoes:
line 3: SAMPLE1_CHIP=5: command not found
line 4: ${SAMPLE${j}_CHIP}: bad substitution
I'm trying to do that in order to name several samples in a while loop changing the "j" parameter.
Anyone knows how to name a variable like that?
It's possible with eval, but don't use dynamic variable names. Arrays are much, much better.
$ j=1
$ SAMPLES[j]=5
$ echo ${SAMPLES[j]}
5
You can initialize an entire array at once like so:
$ SAMPLES=(5 10 15 20)
And you can append with:
$ SAMPLES+=(25 30)
Indices start at 0.
To read the value of the variable, you may use indirection: ${!var}:
#! /bin/bash
j=1
val=get_5
var=SAMPLE${j}_CHIP
declare "$var"="$val"
echo "${!var}"
The problem is to make the variable get the value.
I used declare above, and the known options are:
declare "$var"="$val"
printf -v "$var" '%s' "$val"
eval $var'=$val'
export "$var=$val"
The most risky option is to use eval. If the contents of var or val may be set by an external user, you have set a way to get code injection. It may seem safe today, but after someone edit the code for some reason, it may get changed to give an attacker a chance to "get in".
Probably the best solution is to avoid all the above.
Associative Array
One alternative is to use Associative Arrays:
#! /bin/bash
j=1
val=get_5
var=SAMPLE${j}_CHIP
declare -A array
array[$var]=$val
echo "${array[$var]}"
Quite less risky and you get a similar named index.
Plain array
But it is clear that the safest solution is to use the simplest of solutions:
#! /bin/bash
j=1
val=get_5
array[j]=$val
echo "${array[j]}"
All done, little risk.
If you really want to use variable variables:
#! /bin/bash
j=1
var="SAMPLE${j}_CHIP"
export ${var}=5
echo "${!var}" # prints 5
However, there are other approaches to solving the parent issue, which are likely less confusing than this approach.
j=1
eval "SAMPLE${j}_CHIP=5"
echo "${SAMPLE1_CHIP}"
Or
j=1
var="SAMPLE${j}_CHIP"
eval "$var=5"
echo "${!var}"
As others said, it's normally not possible. Here is a workaround if you wish. Note that you have to use eval when declaring a nested variable, and ⭗ instead of $ when accessing it (I use ⭗ as a function name, because why not).
#!/bin/bash
function ⭗ {
if [[ ! "$*" = *\{*\}* ]]
then echo $*
else ⭗ $(eval echo $(echo $* | sed -r 's%\{([^\{\}]*)\}%$(echo ${\1})%'))
fi
}
j=1
eval SAMPLE${j}_CHIP=5
echo `⭗ {SAMPLE{j}_CHIP}`
c=CHIP
echo `⭗ {SAMPLE{j}_{c}}`

Bash: pass variable as a single parameter / shell quote parameter

I'm writing a bash script which has to pass a variable to another program:
./program $variable
The problem is, it is absolutely necessary for $variable to be passed as a single parameter, which isn't the case if it contains whitespace.
variable=Hello World
./program $variable
-> program receives two arguments: 'Hello' 'World'
Quoting it doesn't do anything at all (well done, bash devs):
variable=Hello World
./program "$variable"
-> program receives: 'Hello' 'World'
Double quoting it does crazy stuff (well done, bash devs):
variable=Hello World
./program "'$variable'"
-> program receives: "'Hello" "World'"
Is there an easy way to do this? Heck, is there a way to do this at all?
Update: Okay, since the problem doesn't seem to be bash, here's some additional info.
The program I'm passing arguments to is a python script. Without modifying the arguments in any way, I get
print sys.argv
-> ['/usr/bin/program', "'Hello", "World'"]
How can I fix that?
Edit: No, I haven't tried
variable="Hello World"
because I never declare $variable. It's not being declared inside my bash function and I'm not allowed to modify it.
Edit: Okay, I got it to work that way.
local temp="$variable"
./program "$temp"
I'd like to know why it works that way and not any other way, though.
did you try with var="hello world"?
i tried this in my solaris box.
> setenv var "hello world"
> cat temp.sh
#!/bin/sh
echo $1
echo $2
> ./temp.sh "$var"
hello world
>
as you can see the $2 is not printed.$var is considered as only one argument.
When you call your script pass the arguments within quotes.
Example script:
#!/bin/bash
for arg in "$#"; do
echo "arg: $1";
shift;
done
When you call it with:
./program "parameter with multiple words" parameter2 parameter3 "another parameter"
The output should be:
arg: parameter with multiple words
arg: parameter2
arg: parameter3
arg: another parameter
Have a look on http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html .
The problem is that the expansion of variables is done before of the command line parameters hence your behavior.
You might work it arround with setting IFS to something weird as
IFS='###' V='foo bar baz'; ./program $V
The problem seems to be inside the "program"
variable="Hello World" # quotes are needed because of the space
./program "$variable" # here quotes again
and inside the program
echo "program received $# arguments:"
i=1
for arg in "$#" # again quotes needed
do echo "arg $((i=i+1)): '$arg'" # and again
done
This is almost certainly a problem in the way you are reading the variable in your program.
For instance suppose this is your script (just one line for testing):
echo "$1"
Let's call it echo.sh. If you run echo.sh "test best", you will get test best.
But if your program says
echo $1
you might get behaviour like what you're seeing.

Print a variable with multi-line value in shell?

In Bash (or other shells) how can I print an environment variable which has a multi-line value?
text='line1
line2'
I know a simple usual echo $text won't work out of the box.
Would some $IFS tweak help?
My current workaround is something like ruby -e 'print ENV["text"]'.
Can this be done in pure shell? I was wondering if env command would take an unresolved var name but it does not seem to.
Same solution as always.
echo "$text"
export TEST="A\nB\nC"
echo $TEST
gives output:
A\nB\nC
but:
echo -e $TEST
A
B
C
So, the answer seems to be the '-e' parameter to echo, assuming that I understand your question correctly.

Expanding variables Bash Scripting

I have two variables in bash that complete the name of another one, and I want to expand it but don't know how to do it
I have:
echo $sala
a
echo $i
10
and I want to expand ${a10} in this form ${$sala$i} but apparently the {} scape the $ signs.
There are a few ways, with different advantages and disadvantages. The safest way is to save the complete parameter name in a single parameter, and then use indirection to expand it:
tmp="$sala$i" # sets $tmp to 'a10'
echo "${!tmp}" # prints the parameter named by $tmp, namely $a10
A slightly simpler way is a command like this:
eval echo \${$sala$i}
which will run eval with the arguments echo and ${a10}, and therefore run echo ${a10}. This way is less safe in general — its behavior depends a bit more chaotically on the values of the parameters — but it doesn't require a temporary variable.
Use the eval.
eval "echo \${$sala$i}"
Put the value in another variable.
result=$(eval "echo \${$sala$i}")
The usual answer is eval:
sala=a
i=10
a10=37
eval echo "\$$sala$i"
This echoes 37. You can use "\${$sala$i}" if you prefer.
Beware of eval, especially if you need to preserve spaces in argument lists. It is vastly powerful, and vastly confusing. It will work with old shells as well as Bash, which may or may not be a merit in your eyes.
You can do it via indirection:
$ a10=blah
$ sala=a
$ i=10
$ ref="${sala}${i}"
$ echo $ref
a10
$ echo ${!ref}
blah
However, if you have indexes like that... an array might be more appropriate:
$ declare -a a
$ i=10
$ a[$i]="test"
$ echo ${a[$i]}
test

Resources