1

I have a CSV that says this:

Source,Target,Database,Table,Is_deletable,Action
DBServer1,DBServer2,DB,TBL1,true,Add
DBServer2,DBServer1,DB,TBL2,true,Add

I have shell script that does this:

while IFS=, read -r source target database table is_deletable action; do
    echo "Building pipeline for ${database}.${table}";
              total=$((total+1))
              Shost="server1.myorganization.com"
              Thost="server2.myorganization.com"

I need Shost to look like this:

Shost = ${'the value of the source column'}
Thost = ${'the value of the target column'}

How do I set this to evaluate dynamically the variable I need based on the value of the column data.

For example:

Shost=${DBServer1}
Thost=${DBServer2}

then on the next loop:

Shost=${DBServer2}
Thost=${DBServer1}

Thanks!

arcee123
  • 101
  • 9
  • 41
  • 118
  • What are you going to do with `$Shost` and `$Thost` variables? – anubhava Mar 03 '22 at 16:27
  • they will be referenced later on in a CURL call. this is a build script. – arcee123 Mar 03 '22 at 16:28
  • the variables are made available in the environmental section of a build engine. in this case Octopus. So in Octopus, I have all of my servers saved, so I can have this script just refer to them dynamically. this let's me keep the code static in different environments (Dev, QA, etc.) – arcee123 Mar 03 '22 at 16:31

3 Answers3

2

Something like this should work for you:

DBServer1='server1.myorganization.com'
DBServer2='server2.myorganization.com'

while IFS=, read -r source target database table is_deletable action; do
   [[ $source = "Source" ]] && continue
   ((total++))
   Shost="${!source}"
   Thost="${!target}"

   # check variables Shost Thost total
   declare -p Shost Thost total
done < file.csv

declare -- Shost="server1.myorganization.com"
declare -- Thost="server2.myorganization.com"
declare -- total="1"
declare -- Shost="server2.myorganization.com"
declare -- Thost="server1.myorganization.com"
declare -- total="2"
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Can you explain how this works? I think you would need to use `eval` somewhere to evaluate `$Shost` recursively. – Barmar Mar 03 '22 at 16:42
  • I think I misunderstood question initially assuming OP wants literally `${DBServer1}`. Corrected it now. Thanks! – anubhava Mar 03 '22 at 16:46
  • thank you anubhava. what is this line: `[[ $source = "Source" ]] && continue` for? – arcee123 Mar 03 '22 at 17:13
  • `[ $source = "Source" ]] && continue` is for skipping first header line in your csv file – anubhava Mar 03 '22 at 17:17
  • 1
    @WilliamPursell My comment preceded (and prompted) the edit that added `${!...}` – Barmar Mar 03 '22 at 17:38
  • Note that, unless you control the name carefully, indirect variable expansion can cause arbitrary code execution. It's as dangerous as `eval`. See this [comment by Stéphane Chazelas](https://unix.stackexchange.com/questions/281390/how-to-get-the-size-of-an-indirect-array-in-bash#comment665388_281390), and the "Risks" section of the [answer by Maëlan](https://stackoverflow.com/a/55331060/4154375) to [Dynamic variable names in Bash](https://stackoverflow.com/q/16553089/4154375). – pjh Mar 03 '22 at 21:32
  • Even if you check that the `source` and `target` values are identifiers there's a danger that they will reference unexpected variables, potentially causing unwanted information leaks. – pjh Mar 03 '22 at 21:33
1

In Bash -- but not necessarily in other shells -- you can reference a variable indirectly, exactly as you seem to want to do:

$ DBServer1=db1.my.com
$ source=DBServer1
$ echo ${source}
DBServer1
$ echo ${!source}
db1.my.com

As the Bash manual puts it (ref):

If [in a parameter expansion of the form ${parameter}] the first character of parameter is an exclamation point (!), and parameter is not a nameref, 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.

Applying that to your sample code and data, we get

DBServer1=server1.myorganization.com
DBServer2=server2.myorganization.com

# ...

while IFS=, read -r source target database table is_deletable action; do
    echo "Building pipeline for ${database}.${table}";
              total=$((total+1))
              Shost="${!source}"
              Thost="${!target}"
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Note that, unless you control the name carefully, indirect variable expansion can cause arbitrary code execution. It's as dangerous as `eval`. See this [comment by Stéphane Chazelas](https://unix.stackexchange.com/questions/281390/how-to-get-the-size-of-an-indirect-array-in-bash#comment665388_281390), and the "Risks" section of the [answer by Maëlan](https://stackoverflow.com/a/55331060/4154375) to [Dynamic variable names in Bash](https://stackoverflow.com/q/16553089/4154375). – pjh Mar 03 '22 at 21:31
  • Even if you check that the `source` and `target` values are valid identifiers there's a danger that they will reference unexpected variables, potentially causing unwanted information leaks. – pjh Mar 03 '22 at 21:32
1

We can also use an associative array for this problem:

declare -A servers=(
    [DBServer1]='server1.myorganization.com'
    [DBServer2]='server2.myorganization.com'
)

{
    read header
    while IFS=, read -r source target database table is_deletable action; do
        Shost=${servers[$source]}
        Thost=${servers[$target]}
        ...
    done
} < file.csv
glenn jackman
  • 238,783
  • 38
  • 220
  • 352