Bash, Concatenating 2 strings to reference a 3rd variable - string

I have a bash script I am having some issues with concatenating 2 variables to call a 3rd.
Here is a simplification of the script, but the syntax is eluding me after reading the docs.
server_list_all="server1 server2 server3";
var1 = "server";
var2 = "all";
echo $(($var1_list_$var2));
This is about as close as I get to the right answer, it acknowledges the string and tosses an error on tokenization.
syntax error in expression (error token is "server1 server2 server3....
Not really seeing anything in the docs for this, but it should be doable.
EDIT: Cleaned up a bit

The Bash Reference Manual explains how you can use a neat feature of parameter expansion to do some indirection. In your case, you're interested in finding the contents of a variable whose name is defined by two other variables:
server_list_all="server1 server2 server3"
var1=server
var2=all
combined=${var1}_list_${var2}
echo ${!combined}
The exclamation mark when referring to combined means "use the variable whose name is defined by the contents of combined"

The Advanced Bash Scripting Guide has the answer for you (http://tldp.org/LDP/abs/html/ivr.html). You have two options, the first is classic shell:
#!/bin/bash
server_list_all="server1 server2 server3";
var1="server";
var2="all";
server_var="${var1}_list_${var2}"
eval servers=\$$server_var;
echo $servers
Alternatively you can use the bash shortcut ${!var}
#!/bin/bash
server_list_all="server1 server2 server3";
var1="server";
var2="all";
server_var="${var1}_list_${var2}"
echo ${!server_var}
Either approach works.

Related

How to put special characters in variable and use them in string?

I'm wondering is there anyway to use for example , or ^ or % and so on, from variables in Bash ?
in instance I have three variables
var1='hello world'
var2=${var1:3}
var3='^'
I want to do this in bash ! please attention to my question I know it's very simple in other ways but how about this ?
echo ${var1:0:3}${var2$var3} # instead of echo ${var1:0:3}${var2^}
and finally output is :
heLlo world
In theory, eval can do execute arbitrary code, but has many security issues, so it should be used as last resort. Use it only when you trust the input 100%.
var1='hello world'
var2=${var1:3}
var3='^'
eval echo '${var1:0:3}${var2'$var3'}'

Unix & Linux ksh shell: How to use eval in CASE expression when variable is derived from a string plus another variable? [duplicate]

How can I retrieve a bash variable value if I have the variable name as string? var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.
Context:
I have some AMI's (Amazon Machine Image) and I want to fire up a few instances of each AMI. As soon as they finish booting, I want to setup each instance according to its AMI type. I don't want to bake lots of scripts or secret keys inside any AMI so I prepared a generalized startup script and I put it on S3 with a publicly accessible link. In rc.local I put small piece of code which fetches the startup script and executes it. This is all I have in the AMIs. Then each AMI accesses a common configuration script which is applicable to all AMIs and special setup scripts for each. These scripts are private and require a signed URL to access them.
So now, when I fire an instance of an AMI (my_private_ami_1), I pass a signed URL for one more file presented on S3 which contains signed URL for all private scripts in terms of key/value pair.config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
When the startup script runs, it downloads the above file and source's it. Then it checks for its AMI type and picks the correct setup script for itself.
ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises
So now I can have a generic code which can fire instances irrespective of their AMI types and instances can take care of themselves.
You can use ${!a}:
var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'
This is an example of indirect parameter expansion:
The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted.
If the first character of parameter is an exclamation point (!), it
introduces a level of variable indirection. Bash uses the value of the
variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.
X=foo
Y=X
eval "Z=\$$Y"
sets Z to foo.
For my fellow zsh users, the way to accomplish the same thing as the accepted answer is to use:
echo ${(P)a} # outputs 'this is the real value'
It is appropriately called Parameter name replacement
This forces the value of the parameter name to be interpreted as a
further parameter name, whose value will be used where appropriate.
Note that flags set with one of the typeset family of commands (in
particular case transformations) are not applied to the value of name
used in this fashion.
If used with a nested parameter or command substitution, the result of
that will be taken as a parameter name in the same way. For example,
if you have ‘foo=bar’ and ‘bar=baz’, the strings ${(P)foo},
${(P)${foo}}, and ${(P)$(echo bar)} will be expanded to ‘baz’.
Likewise, if the reference is itself nested, the expression with the
flag is treated as if it were directly replaced by the parameter name.
It is an error if this nested substitution produces an array with more
than one word. For example, if ‘name=assoc’ where the parameter assoc
is an associative array, then ‘${${(P)name}[elt]}’ refers to the
element of the associative subscripted ‘elt’.
Modified my search keywords and Got it :). eval a=\$$a Thanks for your time.
In bash 4.3+, you can use declare -n:
#!/usr/bin/env bash
var="this is the real value"
var_name="var"
declare -n var_ref=$var_name
echo "${var_ref}"
Had the same issue with arrays, here is how to do it if you're manipulating arrays too :
array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")
ARRAY=$array_name[#]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[#]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"
This will output :
ARRAY=ARRAY_NAME[#]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
In bash 4.3, the '-v' test for set variables was introduced. At the same time, 'nameref' declaration was added. These two features together with the indirection operator (!) enable a simplified version of the previous example:
get_value()
{
declare -n var_name=$1
if [[ -v var_name ]]
then
echo "${var_name}"
else
echo "variable with name <${!var_name}> is not set"
fi
}
test=123
get_value test
123
test="\$(echo \"something nasty\")"
get_value test
$(echo "something nasty")
unset test
get_value test
variable with name <test> is not set
As this approach eliminates the need for 'eval', it is safer.
This code checked under bash 5.0.3(1).
modern shells already support arrays( and even associative arrays). So please do use them, and use less of eval.
var1="this is the real value"
array=("$var1")
# or array[0]="$var1"
then when you want to call it , echo ${array[0]}
Based on the answer: https://unix.stackexchange.com/a/111627
###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument:
# variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
variable_name=$1
variable_value=""
if set | grep -q "^$variable_name="; then
eval variable_value="\$$variable_name"
fi
echo "$variable_value"
}
test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")
VALUE=$(eval "echo \$$SOME_VAR_NAME")
Example
SSH_KEY_FILE_PATH_FOO="/tmp/key"
SSH_KEY_FILE_PATH_VAR_NAME_PREFIX="SSH_KEY_FILE_PATH"
SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX="FOO"
SSH_KEY_FILE_PATH=$(eval "echo \$${SSH_KEY_FILE_PATH_VAR_NAME_PREFIX}_${SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX}")
echo "$SSH_KEY_FILE_PATH"
/tmp/key

How to Read a variable in defined in terminal to a bash script

I am new to shell scripting. I have a very basic question about how we read variables defined in our terminal as input to a shell scirpt.
let us say i defined this variable in my terminal
a=22
if i do echo $a it gives 22 as output in my terminal.
I wanted to pass this variable as a parameter to a script named input.sh
#!/bin/bash
echo "Enter variable name:"
read Input
echo $Input
I ran the scirpt as ./input.sh It popped up the message as
Enter variable name:$a
But in the output i have $a as output not 22. I wanted 22 as output. Is there a way to do this?
You are mixing several things here. Where to start...
The usual way to pass values to a script is through positional parameters.
Suppose you have this script, called s1 :
#!/bin/bash
echo "First two args are: $1 $2"
If you execute it like this :
./s1 Arg1 Arg2
You will see the following output :
First two args are: Arg1 Arg2
If you want to pass a variable name to the script, and have that script output the value of this variable, then you must do two things. First, initialize the variable and export it so that it can be seen by the children processes (including the script you will call, which is a separate process unless called with source or .).
VAR="Some value"
export VAR
You can also do both in a single statement:
export VAR="Some value"
Then, adapt the script to perform an indirect access to the variable :
#!/bin/bash
echo "Value of variable named $1 : ${!1}"
Please note that while $1 means "the content of variable 1", ${!1} means "the content of the variable that is named $1". This is the indirect part.
Now, if you want to take it a step further, and allow the script to interactively read user input (not an argument) and use the value read as a variable name to expand, you would do something like this :
#!/bin/bash
echo "Please enter a variable name"
read VARNAME
echo "Value of variable named $VARNAME : ${!VARNAME}"
Using positional parameters makes the script easier to reuse in non-interactive scenarios, so reading user input should be limited to cases where it is necessary.
The above is to help understand the basics. If you move beyond toy scripts, you will need to understand the security implications of indirect access (especially if you allow user input). You will also need to validate positional parameters or user input are valid for your purpose (i.e. contains a valid variable name) so that you may have your script react appropriately. Well, you would probably need to check if positional parameters were even provided to begin with. All of this is doable in shell scripting, but is beyond the scope of a single question. In any case, checking input (and also errors) will be required if you intend to have robust scripts in situations where reliability is expected.
To use the variables defined in the terminal in your bash script -
Change last line of your script so that it looks like -
#!/bin/bash
echo "Enter variable name:"
read Input
echo ${!Input}
Run your script input.sh on the terminal as -
. input.sh
And finally, when you input the variable name, do not use $ sign. For e.g. -
6c4008a16b7c:~ z001lg8$ . input.sh
Enter variable name:
a
22
6c4008a16b7c:~ z001lg8$
Voila, you can now use the variables defined in the terminal in your script.
Explanation -
In your script, $Input is changed to ${!Input} so that the content of the user input(which is variable name) is echoed and not the variable name itself.
As explained by #Fred - $1 means "the content of variable 1", ${!1} means "the content of the variable that is named $1".
When script is run as . input.sh , it means that you are sourcing the script contents on the terminal. The . symbol is used for sourcing a command/script.
The $ sign is not required when the variable name is entered in terminal because ${!Input} format already takes into account that the value in Input variable is a variable name.

returning values in a bash function

I'm working with a growing bash script and within this script I have a number of functions. One of these functions is supposed to return a variables value, but I am running into some issues with the syntax. Below is an example of the code.
ShowTags() {
local tag=0
read tag
echo "$tag"
}
selected_tag=$(ShowTags)
echo "$selected_tag"
pulled this code from a Linux Journal article, but the problem is it doesn't seem to work, or perhaps it does and im missing something. Essentially whenever the function is called the script hangs up and does not output anything, I need to CTRL+C to drop back to CLI.
The article in question is below.
http://www.linuxjournal.com/content/return-values-bash-functions
So my question is this the proper way to return a value? Is there a better or more dependable way of doing this? And if there is please give me an example so I can figure this out without using global variables.
EDIT:
The behavior of this is really getting to me now. I am using the following script.
ShowTags() {
echo "hi"
local tag=0
read tag
echo "$tag"
}
selected_tag=$(ShowTags)
echo "$selected_tag
Basically what happens is bash will act as if the read command is taking place before the echo tag at the top of the function. As soon as I pass something to read though it will run the top echo, and complete the rest of the script. I am not sure why this is happening. This is exactly what is happening in my main script.
Change echo "hi" to echo "hi" >/dev/tty.
The reason you're not seeing it immediately is that $(ShowTags) captures all the standard output of the function, and that gets assigned to selected_tag. So you don't see any of it until you echo that variable.
By redirecting the prompt to /dev/tty, it's always displayed immediately on the terminal, not sent to the function's stdout, so it doesn't get captured by the command substitution.
You are trying to define a function with Name { ... ]. You have to use name() { ... }:
ShowTags() { # add ()
local tag=0
read tag
echo "$tag"
} # End with }
selected_tag=$(ShowTags)
echo "$selected_tag"
It now lets the user type in a string and have it written back:
$ bash myscript
hello world # <- my input
hello world # script's output
You can add a prompt with read -p "Enter tag: " tag to make it more obvious when to write your input.
As #thatotherguy pointed out, your function declaration syntax is off; but I suspect that's a transcription error, as if it was wrong in the script you'd get different problems. I think what's going on is that the read tag command in the function is trying to read a value from standard input (by default that's the terminal), and pausing until you type something in. I'm not sure what it's intended to do, but as written I'd expect it to pause indefinitely until something's typed in.
Solution: either type something in, or use something other than read. You could also add a prompt (read -p "Enter a tag: " tag) to make it more clear what's going on.
BTW, I have a couple of objections to the linux journal article you linked. These aren't relevant to your script, but things you should be aware of.
First, the function keyword is a nonstandard bashism, and I recommend against using it. myfunc() ... is sufficient to introduce a function definition.
Second, and more serious, the article recommends using eval in an unsafe way. Actually, it's really hard to use eval safely (see BashFAQ #48). You can improve it a great deal just by changing the quoting, and even more by not using eval at all:
eval $__resultvar="'$myresult'" # BAD, can evaluate parts of $myresult as executable code
eval $__resultvar='"$myresult"' # better, is only vulnerable to executing $__resultvar
declare $__resultvar="$myresult" # better still
See BashFAQ #6 for more options and discussion.

How to get a variable value if variable name is stored as string?

How can I retrieve a bash variable value if I have the variable name as string? var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.
Context:
I have some AMI's (Amazon Machine Image) and I want to fire up a few instances of each AMI. As soon as they finish booting, I want to setup each instance according to its AMI type. I don't want to bake lots of scripts or secret keys inside any AMI so I prepared a generalized startup script and I put it on S3 with a publicly accessible link. In rc.local I put small piece of code which fetches the startup script and executes it. This is all I have in the AMIs. Then each AMI accesses a common configuration script which is applicable to all AMIs and special setup scripts for each. These scripts are private and require a signed URL to access them.
So now, when I fire an instance of an AMI (my_private_ami_1), I pass a signed URL for one more file presented on S3 which contains signed URL for all private scripts in terms of key/value pair.config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
When the startup script runs, it downloads the above file and source's it. Then it checks for its AMI type and picks the correct setup script for itself.
ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises
So now I can have a generic code which can fire instances irrespective of their AMI types and instances can take care of themselves.
You can use ${!a}:
var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'
This is an example of indirect parameter expansion:
The basic form of parameter expansion is ${parameter}. The value of
parameter is substituted.
If the first character of parameter is an exclamation point (!), it
introduces a level of variable indirection. Bash uses the value of the
variable formed from the rest of parameter as the name of the
variable; this variable is then expanded and that value is used in the
rest of the substitution, rather than the value of parameter itself.
X=foo
Y=X
eval "Z=\$$Y"
sets Z to foo.
For my fellow zsh users, the way to accomplish the same thing as the accepted answer is to use:
echo ${(P)a} # outputs 'this is the real value'
It is appropriately called Parameter name replacement
This forces the value of the parameter name to be interpreted as a
further parameter name, whose value will be used where appropriate.
Note that flags set with one of the typeset family of commands (in
particular case transformations) are not applied to the value of name
used in this fashion.
If used with a nested parameter or command substitution, the result of
that will be taken as a parameter name in the same way. For example,
if you have ‘foo=bar’ and ‘bar=baz’, the strings ${(P)foo},
${(P)${foo}}, and ${(P)$(echo bar)} will be expanded to ‘baz’.
Likewise, if the reference is itself nested, the expression with the
flag is treated as if it were directly replaced by the parameter name.
It is an error if this nested substitution produces an array with more
than one word. For example, if ‘name=assoc’ where the parameter assoc
is an associative array, then ‘${${(P)name}[elt]}’ refers to the
element of the associative subscripted ‘elt’.
Modified my search keywords and Got it :). eval a=\$$a Thanks for your time.
In bash 4.3+, you can use declare -n:
#!/usr/bin/env bash
var="this is the real value"
var_name="var"
declare -n var_ref=$var_name
echo "${var_ref}"
Had the same issue with arrays, here is how to do it if you're manipulating arrays too :
array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")
ARRAY=$array_name[#]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[#]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"
This will output :
ARRAY=ARRAY_NAME[#]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
In bash 4.3, the '-v' test for set variables was introduced. At the same time, 'nameref' declaration was added. These two features together with the indirection operator (!) enable a simplified version of the previous example:
get_value()
{
declare -n var_name=$1
if [[ -v var_name ]]
then
echo "${var_name}"
else
echo "variable with name <${!var_name}> is not set"
fi
}
test=123
get_value test
123
test="\$(echo \"something nasty\")"
get_value test
$(echo "something nasty")
unset test
get_value test
variable with name <test> is not set
As this approach eliminates the need for 'eval', it is safer.
This code checked under bash 5.0.3(1).
modern shells already support arrays( and even associative arrays). So please do use them, and use less of eval.
var1="this is the real value"
array=("$var1")
# or array[0]="$var1"
then when you want to call it , echo ${array[0]}
Based on the answer: https://unix.stackexchange.com/a/111627
###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument:
# variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
variable_name=$1
variable_value=""
if set | grep -q "^$variable_name="; then
eval variable_value="\$$variable_name"
fi
echo "$variable_value"
}
test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")
VALUE=$(eval "echo \$$SOME_VAR_NAME")
Example
SSH_KEY_FILE_PATH_FOO="/tmp/key"
SSH_KEY_FILE_PATH_VAR_NAME_PREFIX="SSH_KEY_FILE_PATH"
SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX="FOO"
SSH_KEY_FILE_PATH=$(eval "echo \$${SSH_KEY_FILE_PATH_VAR_NAME_PREFIX}_${SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX}")
echo "$SSH_KEY_FILE_PATH"
/tmp/key

Resources