Unable to use constant value in a variable - shell

I am writing a small script in shell and I have a constant in upper cases as you can see in code ('PRIX'). My problem is, I would like to print the value of the constant using the variable that will be equal to the string ('PRIX'). I won't explain you why as it would be useless, but that would cut my entire code in half and simplify everything.
readonly _PRIX_=7
.
.
.
function trier {
#here it starts
#function call, change 'option' value for "_PRIX_"
option=$(equivalence "$option")
#HERE The line I need to fix to print '7'
echo $'$option'
#Print '7', What I wanna recreate knowing 'option' value=="_PRIX_"
echo $_PRIX_
sort -t$":" -k'$option' "$le_depot"
}

If you are using Bash, indirection might help you. For example:
readonly _PRIX=7
option=PRIX
option_var=_$option
echo "${!option_var}" # outputs 7
Related:
Dynamic variable names in Bash
What is indirect expansion? What does ${!var*} mean?

Related

Combine a variable and string and get the value of the variable formed in a single line

I was writing a script where I came across a situation.
Audio_Repo = "/src/audio_123";
Audio_ImgTag = "aud021882";
Audio_Enable = 1;
.....
Video_Repo = "/src/vid_823";
Video_ImgTag = "video9282";
Video_Enable = 0;
....
#Say proj_var ="Audio"
#it could be either Audio or Video based on some conditional check
....
proj_var = "Audio"
....
PROJECT_REPO= ${!{$proj_var"_Repo"}}
#PROJECT_REPO should hold the value "src/audio_123"
But the above representation throws bad substitution error
I know that I could use a temporary variable as follows
temp= $proj_var"_Repo";
PROJECT_REPO = ${!temp};
But I have many properties and I do not want to use temporary variables for each of them. Instead I want single line substitutions.
One way to do it is to use eval:
#! /bin/bash -p
Audio_Repo="/src/audio_123"
Audio_ImgTag=aud021882
Audio_Enable=1
# ...
Video_Repo=/src/vid_823
Video_ImgTag=video9282
Video_Enable=0
# ....
# Say proj_var="Audio"
# it could be either Audio or Video based on some conditional check
# ....
proj_var="Audio"
# ....
eval "Project_Repo=\${${proj_var}_Repo}"
# Project_Repo should hold the value "src/audio_123"
printf '%s\n' "$Project_Repo"
The code prints /src/audio_123.
eval is dangerous, and should be avoided if possible. See Why should eval be avoided in Bash, and what should I use instead?. In this case the temporary variable, despite the increased verbosity, is a better option.
I replaced PROJECT_REPO with Project_Repo to avoid possible a possible clash with an environment variable. See Correct Bash and shell script variable capitalization.
I've fixed some Bash syntax issues in the code in the question. Spaces around = are errors. Semicolons at the ends of lines are unnecessary.
Shellcheck issues some warnings for the code, but they are all harmless.
Another option is to use a helper function:
# ...
# Set the value of the variable whose name is in the first parameter ($1)
# to the value of the variable whose name is in the second parameter ($2).
function setn { printf -v "$1" '%s' "${!2}" ; }
# ...
setn Project_Repo "${proj_var}_Repo"
Using the setn (a poor name, choose a better one) function avoids both a temporary variable and eval.
Uses arrays, not variable names you need to manipulate.
Repo=0
ImgTag=1
Enable=2
Audio=(/src/audio_123 aud021882 1)
Video=(/src/vid_823 video9282 0)
proj_repo=Audio[$Repo]
project_var=${!proj_repo}

Convert Array Element to Grab Clone Variable Name [duplicate]

This question already has answers here:
What is indirect expansion? What does ${!var*} mean?
(6 answers)
Closed 5 years ago.
I'm trying to take elements from $Table_Header_Labels in a loop and have it grab the string of the same name to be used for string length. Ex: Grabbed "Apple" from the array and then convert it to grab $Apple to get the string to do ${#Apple}.
#!/bin/bash
# Create Array
declare -a Array=('Red' 'Blue' 'White');
# Set Variable
Red="Apples are Red!"
# Get Size of Variable from Array Element
for i in ${Array[0]}
do
Element=${i}
Element_Size="${i}_Size"
# Print the generated one
echo "$Element_Size = ${#Element}"
# Print correct one to compare
echo "Red_Size = ${#Red}"
echo ""
done
Please take note I purposely made it so the loop only goes once. Currently, the Red_Size counts the array element itself which is three. I'm trying to get it to take the array element and grab the string assigned to the variable that is replicated by the array element.
Basic break down explanation: Apple => $Apple
Is this even possible with BASH?
To get the value of a variable whose name is in a variable,
you can use the ${!name} syntax.
For example, if i contains the name of a variable, "Red",
to get the value of the variable Red,
you can write ${!i}.
In your script:
Element=${!i} # if $i is "Red" -> $Red -> "Apples are Red!"

Add up parameters in for loop

I've implemented a function which contains a while for loop defined as follows:
func()
{
...
for i in "$#"; do
enum="source"
sourceID=$i
ret_ID $FILE_NAME $sourceID $enum
ID=$req_ID
((ID+=ID))
done
echo $ID
}
The function ret_ID parses a file which contains some variables as
param1=0x00000001
param2=0x00000400
param3=0x000008000
...
No matter how many parameters I pass, echo $ID returns the ID associated with the last parameter and not the sum of all of them. For instance, func param1 param3 returns 32768 and not 32769.
Update: Judging by a comment by the OP, it sounds like glenn jackman's recommendation to switch from letting ret_ID() set a global variable in order to return its result to outputting its result to stdout and capturing the result in a command substitution ($(...)) solved the problem.
Assuming that the problem wasn't the simple logic error Glenn points out (((ID+=ID)) should be ((ID+=req_ID )): The exact cause of the original problem is not known, but since both func() and ret_ID() operate on global variables, it's easy to see how one function could interfere with the other, such as if ret_ID() accidentally also sets variable $ID.
Here's a rewrite of the function that shows this change, and also suggests a few other changes to make the function more robust, most notably the use of local variables:
func()
{
# Declare *local* variables.
local arg enum sourceID retID
# Declare the local result variable *as an integer*
# Also, better to use variable names that at least start with a *lowercase*
# letter to avoid conflicts with *environment* variables.
local -i id=0
# ...
for arg; do # loop over all args; the `in "$#"` part is optional
enum="source"
sourceID=$arg
# Call helper function and let it return its result via *stdout* captured
# through a command substitution rather than by setting a global variable.
# Note the use of double quotes to prevent problems with values with embedded spaces.
reqID=$(ret_ID "$FILE_NAME" "$sourceID" "$enum")
# Add the value returned to $id
# Note that since $id was declared as an integer,
# use of (( ... )) is optional.
id+=$reqID
done
echo "$id"
}

Change a referenced variable in BASH

I am intending to change a global variable inside a function in BASH, however I don't get a clue about how to do it. This is my code:
CANDIDATES[5]="1 2 3 4 5 6"
random_mutate()
{
a=$1 #assign name of input variable to "a"
insides=${!a} #See input variable value
RNDM_PARAM=`echo $[ 1 + $[ RANDOM % 5 ]]` #change random position in input variable
NEW_PAR=99 #value to substitute
ARR=($insides) #Convert string to array
ARR[$RNDM_PARAM]=$NEW_PAR #Change the random position
NEW_GUY=$( IFS=$' '; echo "${ARR[*]}" ) #Convert array once more to string
echo "$NEW_GUY"
### NOW, How to assign NEW_GUY TO CANDIDATES[5]?
}
random_mutate CANDIDATES[5]
I would like to be able to assign NEW_GUY to the variable referenced by $1 or to another variable that would be pointed by $2 (not incuded in the code). I don't want to do the direct assignation in the code as I intend to use the function for multiple possible inputs (in fact, the assignation NEW_PAR=99 is quite more complicated in my original code as it implies the selection of a number depending the position in a range of random values using an R function, but for the sake of simplicity I included it this way).
Hopefully this is clear enough. Please let me know if you need further information.
Thank you,
Libertad
You can use eval:
eval "$a=\$NEW_GUY"
Be careful and only use it if the value of $a is safe (imagine what happens if $a is set to rm -rf / ; a).

Clone a variable in bash?

Before explaining my bash problem let me give you some context:
I am writing some scripts using a bash "framework" we use at my current job. One of the feature of the framework is to init a sets of environments variables useful to run jobs on our cluster infrastructure.
These variables depend on a date specified by $YY, $mm and $dd which are also environment variables (yes, this is wired). To use the framework you start by defining the date and then you call a function to init other vars. This works fine when you write scripts that need variables for a specific day only. Today I am writing something which needs variables for 2 different days. Writing this I face a strange issue. For you to better understand the problem I wrote this code that simulate it:
#!/bin/bash
function assign(){
date=$1
date[1]=$YY
date[2]=$mm
date[3]=$dd
}
function display() {
date=$1
echo "${date[1]}/${date[2]}/${date[3]}"
}
export YY=2012
export mm=09
export dd=20
declare -a my_date1=()
assign $my_date1
export YY=2012
export mm=08
export dd=20
declare -a my_date2=()
assign $my_date2
display $my_date1
display $my_date2
The expected output is:
2012/09/20
2012/08/20
But the output is:
2012/08/20
2012/08/20
At first I thought that the assign function filled the array with reference to $YY, $mm and $dd instead of their values. But then I try with the following code and it doesn't change the result.
date[1]=$(echo $YY)
date[2]=$(echo $mm)
date[3]=$(echo $dd)
Can somebody explain me what append?
Maybe something wired with date=$1...
Arrays are not passed by either value or reference in bash. Rather, the value of the expansion of the array is passed by value. When you write
assign $my_date1
the date variable inside assign is null, since $my_date1 expands to an empty string and disappears after word-splitting before the function is called. As a result, $1 is unset.
But date, being a global variable because it was not declared as local, is set correctly using YY et al, then reset on the second call to assign.
Also, note that the first line of your functions does not make date a reference to the argument; it's really just setting the 0th element of what becomes the global date array to the expansion of $1.
Having said I'll that, I'll show you a way to fake it using the declare built-in and indirect parameter expansion.
function assign () {
ref=$1
# Without the -g, we'd declare function-local parameters. The argument is a
# string to evaluate as a variable assignment. If $ref=my_date1, then we do
# 'my_date1[1]=$YY', 'my_date1[2]=$mm', etc.
declare -g "$ref[1]=$YY"
declare -g "$ref[2]=$mm"
declare -g "$ref[3]=$dd"
}
function display () {
ref=$1
# If $ref=my_date1, then idx
# iterates over my_date[1], my_date[2], my_date[3].
# E.g. ${!idx} in the first iteration is ${my_date[1]}.
arr=()
for idx in $ref[{1,2,3}]; do
arr+=( ${!idx} )
done
local IFS="/"
echo "${arr[*]}"
}
export YY=2012 mm=09 dd=20
assign my_date1 # The *name* of the array; no need to predeclare
export YY=2012 mm=08 dd=20
assign my_date2 # The *name* of the array; no need to predeclare
# Again, just the *names* of the arrays
display my_date1
display my_date2

Resources