How to define dynamic variable in bash? [duplicate] - bash

This question already has answers here:
How to create a bash variable like $RANDOM
(3 answers)
Closed 5 years ago.
I would like to have a shell variable that could be dynamically run every time it is refered, for example, i would like to have a variable $countPwd which could return the count of files/dirs in the current directory, it could be defined as:
countPwd=`ls | wc -l`
and if I do echo $countPwd it would only show the value when I define the variable, but it won't update automatically when I change my current directory. So how do I define such a variable in bash that the value of it get updated/calculated on the fly?
Update:
The $PWD is a perfect example of a variable get evaluated in the real time. You don't need to use $() or backticks `` to evaluate it. How is it defined in bash?

Make a function:
countPwd() {
ls | wc -l
}
Then call the function like any other command:
echo "There are $(countPwd) files in the current directory."

Another option: store the command in a variable, and evaluate it when required:
countPwd='ls | wc -l'
echo $(eval "$countPwd")

You'll need to write some C code: write a bash's loadable buitins to do the heavy lifting in defining variables with dynamic values like $SECONDS or $RANDOM ($PWD is just set by cd).
More details in my answer here (a duplicate of this question).

Related

create multiple global variables from a loop in bash [duplicate]

This question already has answers here:
Indirect variable assignment in bash
(7 answers)
Dynamic variable names in Bash
(19 answers)
Closed 4 years ago.
So I have a loop that basically goes through all of the disks installed on the system, then it assigns a variable to a disk name. however, I cannot use those variables if it's not inside the loop, how do I make them available to be used in other functions or other parts of the script?
Here is the code
#!/bin/bash
dev=1
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
set "DISK$dev=$disk"
dev=$((dev+1))
done
So if I do echo $DISK1 for example, it doesn't display anything.
But if I do echo $DISK1 INSIDE the loop, then ir displays the fist variable assignment. Can I export them and make them available outside of the loop?
set is not at all the right command to use here. You could pull this off with eval where you have set; but the proper way to solve this is simply to assign the values to an array.
disks=($(fdisk -l | grep -o '/dev/sd[a-z]'))
You can loop over the individual entries with ${disk[0]} through ${disk[n]} or retrieve the entire array at once with "${disk[#]}". The expression ${#disk[#]} evaluates to the number of elements in the array; though because array indexing is zero-based, the last index is one less than this value.
Of course, very often, you don't really need to keep the results in a variable explicitly. If you don't need random access (do you need to recall what you know about the first disk when processing the fifth? Really?) you should probably just loop over the values directly.
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
: whatever you need to do with the current "$disk"
done
You cannot directly access the parent scope. Also with "export" you will be able to access the exported variable to sub-shell but not the parent one.
A work around for this can be put all these set into a file (appending the new sets) and execute it with after the loop:
dev=1
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
echo "export \"DISK$dev=$disk\";" >> my_script_full_of_sets.sh
dev=$((dev+1))
done
. my_script_full_of_sets.sh

how to use variable variable names in bash script [duplicate]

This question already has answers here:
How to use a variable's value as another variable's name in bash [duplicate]
(6 answers)
Closed 6 years ago.
I'm trying to get the values of some php variables into a bash script in order to set other bash variables. Basically I need to be able to access the value of a variable - variable name.
the script can find & read the file, find the php variables, but when I try to set them it just hangs. Here is what I have:
variables=(database_user database_password dbase);
paths=(modx_core_path modx_connectors_path modx_manager_path modx_base_path);
for index in "${variables[#]}"; do
index=\$$index
echo $index;
$index="$(grep -oE '\$${!index} = .*;' $config | tail -1 | sed 's/$${!index} = //g;s/;//g')";
done
not sure what I am doing wrong here...
You are trying to perform an indirect assignment.
You should get rid of these two lines :
index=\$$index
echo $index;
By simply writing :
echo "${!index}"
Which does an indirect expansion cleanly (gives you the value of the variable whose name is contained in variable index).
Next, the problematic line is this one:
$index="$(grep -oE '\$${!index} = .*;' ... (rest omitted)
In Bash, an assignment cannot begin with a $.
One way you can perform an indirect assignment is this (after removing the index re-assignment as suggested above) :
printf -v "$index" "$(grep -oE '\$${!index} = .*;' ... (rest omitted)
This uses the -v option of printf, which causes the value passed as the final argument to be assigned to a variable, the name of which is passed to the -v option.
There are other ways of handling indirect assignment/expansions, some with code injection risks, as they use (potentially uncontrolled) data as code. This is something you may want to research further.
Please note I am assuming the actual grep command substitution works (I have not tested).

Double expansion of a parameter in Bash script [duplicate]

This question already has answers here:
Lookup shell variables by name, indirectly [duplicate]
(5 answers)
Closed 7 years ago.
I want to expand a parameter twice, and ${$PARAM} doesn't work.
For example, if I set two variables to file names and then want to loop through those variables and expand to their file names:
INPF_1=input1.inp
INPF_2=input2.inp
# copy the input files to the execution directory
for input_param in ${!INPF_*}; do
echo ${$input_param}
done
How can I to access the file names from those parameters in the for loop?
I.e., expand to input1.inp and input2.inp.
You had it almost: just use "${!input_param}" instead of ${$input_param}.
The quoting doesn't do anything in this case, but it's a good habit.
Be aware of the difference to ${!INPF_#}, which – when used between double quotes – expands to a separate word for each variable name, whereas "${!INPF_*}" doesn't. You almost always want "${!INPF_#}". See the difference:
$ for var in "${!INPF_*}"; do echo "$var"; done
INPF_1 INPF_2
$ for var in "${!INPF_#}"; do echo "$var"; done
INPF_1
INPF_2
See the manual for the various parameter expansions.

Unable to print value of shell variable stored in another variable [duplicate]

This question already has answers here:
Lookup variable value from string in shell script
(2 answers)
Closed 8 years ago.
I'm trying to find all variables which match a particular pattern and print their values.
test_a="apple"
test_b="banana"
test_c="carrot"
test_d="doughnut"
test_show_all () {
local i
for i in ${!test_*}; do
printf "..$i\n"
# printf "..$i-->${$i}\n"
done
}
The loop is finding the correct variables.
But if I uncomment the second line of the for loop, bash is unhappy with the syntax ${$i}. I thought this should work since $i holds the name of a variable, so I thought ${$i} should expand to value of that stored name.
The indirect variable reference is ${!var}. Change your code to
printf "..$i-->${!i}\n"
and it should work.
Not useful now, but in bash 4.3, you will also be able to use namerefs.
for name in ${!test_*}; do
declare -n value=$name
printf "..$name->$value\n"
done
As a short cut, if you only want to iterate over the values (without regard to the actual name of the variable), you can use
declare -n value
for value in ${!test_*}; do
printf "..$value\n"
done

Bash script execute shell command with Bash variable as argument

I have one loop that creates a group of variables like DISK1, DISK2... where the number at the end of the variable name gets created by the loop and then loaded with a path to a device name. Now I want to use those variables in another loop to execute a shell command, but the variable doesn't give its contents to the shell command.
for (( counter=1 ; counter<=devcount ; counter++))
do
TEMP="\$DISK$counter"
# $TEMP should hold the variable name of the disk, which holds the device name
# TEMP was only for testing, but still has same problem as $DISK$counter
eval echo $TEMP #This echos correctly
STATD$counter=$(eval "smartctl -H -l error \$DISK$counter" | grep -v "5.41" | grep -v "Joe")
eval echo \$STATD$counter
done
Don't use eval ever, except maybe if there is no other way AND you really know what you are doing.
The STATD$counter=$(...) should give an error. That's not a valid assignment because the string "STATD$counter" is not a valid variable name. What will happen is (using a concrete example, if counter happened to be 3 and your pipeline in the $( ) output "output", bash will only expand that line as far as "STATD3=output" so it will try to find a command named "STATD3=output" and run it. Odds are this is not what you intended.
It sounds like everything you want to do can be accomplished with arrays instead. If you are not familiar with bash arrays take a look at Greg's Wiki, in particular this page or the bash man page to find out how to use them.
For example, in the loop you didn't post in your question: make disk (not DISK: don't use all upper case variable names) an array like so
disk+=( "new value" )
or even
disk[counter]="new value"
Then in the loop in your question, you can make statd an array as well and assign it with values from disk by
statd[counter]="... ${disk[counter]} ..."
It's worth saying again: avoid using eval.

Resources