Iterating an ordered path variable in a for loop - bash

I have the following txt document:
deviceIDs.txt
UDID0=LGH811dec0bfd6
UDID1=41006289e4b2a179
UDID2=d9a7aa45
PORT0=4567
PORT1=4568
PORT2=4569
BOOTPORT0=5556
BOOTPORT1=5557
BOOTPORT2=5558
I want to be able to write the following bash script:
#!/bin/bash
source /path/deviceIDs.txt
for ((i=0;i<=2;i++))
do
echo $UDID$i
echo $PORT$i
echo $BOOTPORT$i
done
however this doesn't work. Presumable because the dollar sign is being used twice in one expression. I've tried formatting the variables using {} too but still no success.

You can use bash's form of indirection:
#!/bin/bash
source deviceIDs.txt
for ((i=0;i<=2;i++))
do
for x in UDID PORT BOOTPORT
do
y=$x$i
echo ${!y}
done
done
The key here is that ${!y} returns the value of the variable that is named by y.
This produces the output:
LGH811dec0bfd6
4567
5556
41006289e4b2a179
4568
5557
d9a7aa45
4569
5558
Alternative
If we are allowed to change the data file format, consider defining your variables as arrays this way:
$ cat IDarrays.txt
UDID=(LGH811dec0bfd6 41006289e4b2a179 d9a7aa45)
PORT=(4567 4568 4569)
BOOTPORT=(5556 5557 5558)
With that, the script can be written:
#!/bin/bash
source IDarrays.txt
for ((i=0;i<=2;i++))
do
echo ${UDID[$i]}
echo ${PORT[$i]}
echo ${BOOTPORT[$i]}
done

I didn't knew John1024's solution with !y.
If you can't modify deviceIDs.txt because it is generated by someone out of your control, you could use arrays like this too:
#!/bin/bash
source /path/deviceIDs.txt
u=($UDID{0..2});p=($PORT{0..2});b=($BOOTPORT{0..2})
for i in {0..2}; do echo -e ${u[i]}"\n"${p[i]}"\n"${b[i]}"\n"; done
LGH811dec0bfd6
4567
5556
41006289e4b2a179
4568
5557
d9a7aa45
4569
5558

Related

Change name of Variable while in a loop

I have this idea in mind:
I have this number: CN=20
and a list=( "xa1-" "xa2-" "xb1-" "xb2-")
and this is my script:
for a in "${list[#]}"; do
let "CN=$(($CN+1))"
echo $CN
Output:
21
22
23
24
I am trying to create a loop where it creates the following variables, which will be referenced later in my script:
fxp0_$CN="fxp-$a$CN"
fxp0_21="fxp-xa1-21"
fxp0_22="fxp-xa2-22"
fxp0_23="fxp-xb1-23"
fxp0_24="fxp-xb2-24"
However, I have not been able to find a way to change the variable name within my loop. Instead, I was trying myself and I got this error when trying to change the variable name:
scripts/srx_file_check.sh: line 317: fxp0_21=fxp0-xa2-21: command not found
After playing around I found the solution!
for a in "${list[#]}"; do
let "CN=$(($CN+1))"
fxp_int="fxp0-$a$CN"
eval "fxp0_$CN=${fxp_int}"
done
echo $fxp0_21
echo $fxp0_22
echo $fxp0_23
echo $fxp0_24
echo $fxp0_25
echo $fxp0_26
echo $fxp0_27
echo $fxp0_28
Output:
fxp0-xa1-21
fxp0-xa2-22
fxp0-xb1-23
fxp0-xb2-24
fxp0-xc1-25
fxp0-xc2-26
fxp0-xd1-27
fxp0-xd2-28
One common method for maintaining a dynamically generated set of variables is via arrays.
When the variable names vary in spelling an associative array comes in handy whereby the variable 'name' acts as the array index.
In this case since the only thing changing in the variable names is a number we can use a normal (numerically indexed) array, eg:
CN=20
list=("xa1-" "xa2-" "xb1-" "xb2-")
declare -a fxp0=()
for a in "${list[#]}"
do
(( CN++ ))
fxp0[${CN}]="fxp-${a}${CN}"
done
This generates:
$ declare -p fxp0
declare -a fxp0=([21]="fxp-xa1-21" [22]="fxp-xa2-22" [23]="fxp-xb1-23" [24]="fxp-xb2-24")
$ for i in "${!fxp0[#]}"; do echo "fxp0[$i] = ${fxp0[$i]}"; done
fxp0[21] = fxp-xa1-21
fxp0[22] = fxp-xa2-22
fxp0[23] = fxp-xb1-23
fxp0[24] = fxp-xb2-24
As a general rule can I tell you that it's not a good idea to modify names of variables within loops.
There is, however, a way to do something like that, using the source command, as explained in this URL with some examples. It comes down to the fact that you treat a file as a piece of source code.
Good luck

in bash use parameter inside another parameter

I have list of parameters that take from a property file looks like that:
db_instanceid=i-0c2b12ae02d454018
db_secgrp=sg-8c2efcf3
backend_instanceid=i-0199621ba358d1814
backend_secgrp=sg-5e508221
frontend_instanceid=i-0199621ba358d1814
frontend_secgrp=sg-e152809e
in bash, I want to use an array to perform actions using those parameters,
Something like that:
declare -a arr=("frontend" "backend" "db")
for i in "${arr[#]}"
do
inter=$i
echo "Get PublicIp for $inter server"
echo "$inter security group - $inter_secgrp" ;
done
But $inter_secgrp itself will be read as a parameter,
so I get an empty string (which make sense). How can I read this parameter
the right way so I will get the value of all "secgrp"
needed output:
Get PublicIp for frontendserver
frontendserver security group - sg-e152809e
Get PublicIp for backend
backendsecurity group - sg-5e508221
Get PublicIp for db
dbsecurity group - sg-8c2efcf3
Could do it with indirect expansion
. parameterfile
declare -a arr=("frontend" "backend" "db")
for inter in "${arr[#]}"
do
echo "Get PublicIp for $inter server"
inter_secgrp=${inter}_secgrp
echo "$inter security group - ${!inter_secgrp}" ;
done
The temptation to use eval is particularly strong here.
. parameterfile
declare -a arr=("frontend" "backend" "db")
for inter in "${arr[#]}"
do
echo "Get PublicIp for $inter server"
eval inter_secgrp=\$${inter}_secgrp
echo "$inter security group - $inter_secgrp" ;
done
And then $inter_secgrp is what you want.
To be simple, eval tells Bash to evaluate the line for an extra time before executing it. Here after the first evaluation, the eval line turns to (for example
eval inter_secgrp=$db_secgrp
And then things after eval is evaluated again, so $db_secgrp gets expanded, giving what you want.

bash for script and input parameter

Can anyone help me to modify my script. Because it does not work. Here are three scripts.
1) pb.sh, use delphicpp_release software to read the 1brs.ab.sh and will give the output as 1brs.ab.out
2) 1brs.ab.sh, use for input parameter where a.sh(another script for protein structure), chramm.siz, charmm.crg are file for atom size and charge etc. rest of the parameters for run the delphicpp_release software.
3) a.sh, use for read several protein structures, which will be in the same directory.
my script_1 = pb.sh:
./delphicpp_release 1brs.ab.sh >1brs.ab.out
echo PB-Energy-AB = $(grep -oP '(?<=Energy> Corrected:).*' 1brs.ab.out) >>PB-energy.dat
cat PB-energy.dat
script_2 = 1brs.ab.sh:
in(pdb,file="a.sh")
in(siz,file="charmm.siz")
in(crg,file="charmm.crg")
perfil=70
scale=2.0
indi=4
exdi=80.0
prbrad=1.4
salt=0.15
bndcon=2
maxc=0.0001
linit=800
energy(s)
script_3 = a.sh:
for i in $(seq 90000 20 90040); do
$i.pdb
done
As we don't know what software is, something like
for ((i=90000;i<=100000;i+=20)); do
./software << " DATA_END" > 1brs.$i.a.out
scale=2.0
in(pdb,file="../$i.ab.pdb")
in(siz,file="charmm.siz")
in(crg,file="charmm.crg")
indi=z
exdi=x
prbrad=y
DATA_END
echo Energy-A = $(grep -oP '(?<=Energy>:).*' 1brs.$i.a.out) >>PB-energy.dat
done
A more POSIX shell compliant version
i=90000
while ((i<=100000)); do
...
((i+=20));
done
EDIT: Without heredoc
{
echo 'scale=2.0'
echo 'in(pdb,file="../'"$i"'.ab.pdb")'
echo 'in(siz,file="charmm.siz")'
echo 'in(crg,file="charmm.crg")'
echo 'indi=z'
echo 'exdi=x'
echo 'prbrad=y'
} > $i.ab.sh
./software <$i.ab.sh >$i.ab.out
but as question was changed I'm not sure to understand it.

Cannot evaluate script arguments from function

I've started writing shell scripts again and I've found myself in a situation where I frequently have to write debug echo's to trace what the script is doing. The, easy, way I used to do this right was to write something like this :
#!/bin/bash
myVar = 'Erractic Nonesense'
echo "myVar: $myVar"
==> myVar: Erractic Nonesense
This worked great and was fairly simple but, having to write this for every variable I wished to trace was tiring and as a person who thinks that having less code to do more stuff is great, I wrote myself a function:
#!/bin/bash
dbg() # $msg
{
echo "$#: ${!#}"
}
myVar = 'Erractic Nonesense'
dbg myVar
==> myVar: Erractic Nonesense
This works great for regular variables but, for the scripts arguments ($1, $2, etc.), does not work. Why?
==> $ ./myScript 123
#!/bin/bash
...
dbg 1 # This is the bugger in question.
==> 1: 1
And also, how can this be circumvented?
EDIT
Thanks to Barmar I now see why it behaves this way but, the second question remains.
EDIT 2
Using koodawg idea, this is the result. It works. Updated, see EDIT 4
EDIT 3
I think that a mix of EDIT 2 and set +-x will be a viable solution.
EDIT 4
Updated the logic to fall on arguments as the previous one did not always worked. Added fancy tabs.
RX_INTEGER='^[0-9]+$'
DBG_SCRIPT_ARGS=( "$0" "$#" )
DBG_PADDING=" " # tabs of 8 spaces
dbg() # $msg | OUT$args OUT$res
{
args=$#
[[ $args =~ $RX_INTEGER ]] && res="${DBG_SCRIPT_ARGS[args]}" || res="${!#}"
printf "%s%s\`%s\`\n" "$args:" "${DBG_PADDING:$(((${#args}-1)%${#DBG_PADDING}))}"
}
You would have to call the function and explicitly pass the script args to it. You could do something like:
for argz in `echo $*`
do
dbg ${argz}
done

Iterating over variable name in bash script

I needed to run a script over a bunch of files, which paths were assigned to train1, train2, ... , train20, and I thought 'why not make it automatic with a bash script?'.
So I did something like:
train1=path/to/first/file
train2=path/to/second/file
...
train20=path/to/third/file
for i in {1..20}
do
python something.py train$i
done
which didn't work because train$i echoes train1's name, but not its value.
So I tried unsuccessfully things like $(train$i) or ${train$i} or ${!train$i}.
Does anyone know how to catch the correct value of these variables?
Use an array.
Bash does have variable indirection, so you can say
for varname in train{1..20}
do
python something.py "${!varname}"
done
The ! introduces the indirection, so "get the value of the variable named by the value of varname"
But use an array. You can make the definition very readable:
trains=(
path/to/first/file
path/to/second/file
...
path/to/third/file
)
Note that this array's first index is at position zero, so:
for ((i=0; i<${#trains[#]}; i++)); do
echo "train $i is ${trains[$i]}"
done
or
for idx in "${!trains[#]}"; do
echo "train $idx is ${trains[$idx]}"
done
You can use array:
train[1]=path/to/first/file
train[2]=path/to/second/file
...
train[20]=path/to/third/file
for i in {1..20}
do
python something.py ${train[$i]}
done
Or eval, but it awfull way:
train1=path/to/first/file
train2=path/to/second/file
...
train20=path/to/third/file
for i in {1..20}
do
eval "python something.py $train$i"
done

Resources