0

I hope you are doing well.

I am a bit confused...

I have a file like:

var1:value1
var2:value2
...
varn:blabla/$var2/blabla

When I read the file, no problem I can get all lines, and instantiate all the variables.

In brief this is what I do in a function:

while IFS=":" read -r f1 f2
do
    if [ ! -z $f1 ] && [ ! -z $f2 ]; then

        f2=eval $f2
        export $f1=$f2

    fi
done < $fileName

And it works well except for varn...

I get blabla/$var2/blabla

instead of having:

blabla/value2/blabla...

and this makes my script failing to start a command since varn is supposed to be the path.

the real parameter file is:

<deleted some parameters>
version:1.7.0

MongoInstall:/apps/deliverable_${version}/mongo

during execution I get:

+ export 'MongoInstall=/ec/local/nasstat/apps/deliverable_${version}/mongo'
+ MongoInstall='/ec/local/nasstat/apps/deliverable_${version}/mongo'

...

and then...

+ '/apps/deliverable_${version}/mongo/installation_mongo_1.7.0.sh' env stop
./mongotransfer.sh: line 54: /apps/deliverable_${version}/mongo/installation_mongo_1.7.0.sh: No such file or directory

While I would expect to get:

/apps/deliverable_1.7.0/mongo/installation_mongo_1.7.0.sh

Thank you

8
  • Yes well spotted Stéphane. So, to read: varn:blabla/$var2/blabla Commented Aug 5, 2022 at 14:16
  • That f2=eval $f2 line makes little sense. Also note that in bash, parameter expansions must be quoted. See When is double-quoting necessary? Commented Aug 5, 2022 at 14:24
  • Are you sure this works? Are you really running f2=eval $f2? Did you mean f2=$(eval "$f2") maybe? Or something else? f2=eval $f2 will first set the variable f2 to the string eval, temporarily for the duration of the current command, and then will try to execute the original value of $f2 as a command.
    – terdon
    Commented Aug 5, 2022 at 14:24
  • @terdon, eval "$f1=\$f2" or maybe eval "$f1='$f2'" would make more sense even if still dangerous and not foolproof. Commented Aug 5, 2022 at 14:26
  • I need f2 to be evaluated and then assigned to f1 which is then exported. Yes it works for all the variables except that one. Commented Aug 5, 2022 at 14:28

1 Answer 1

1

If switching to zsh is an option, you could do:

set -o extendedglob -o nounset
unset MongoInstall
while IFS= read -r line; do
  [[ $line = (#b)([[:IDENT:]]##):(*) ]] &&
    : ${(P)match[1]::=${(e)match[2]}}
done < parameter-file
printf 'MongoInstall="%s"\n' $MongoInstall

Beware the e flag, like your eval will happily evaluate code in there. For instance, if the parameter file contains var=$(reboot), it will reboot.

A safer approach, would be to do the expansions by hand:

MongoInstall=$(
  perl -wlne '
    if (/^(\w+):(.*)/) {
      $var = $1; $value = $2;
      $value =~ s/\$\{(\w+)\}/$var{$1}/g;
      $var{$var} = $value;
    }
    END {print $var{MongoInstall}}' < parameter-file
)

Or the same with zsh:

set -o extendedglob -o nounset
typeset -A vars
while IFS= read -r line; do
  if [[ $line = (#b)([[:IDENT:]]##):(*) ]]; then
    var=$match[1] value=$match[2]
    value=${value//(#b)\${([[:IDENT:]]##)}/$vars[$match[1]]}
    vars[$var]=$value
  fi
done < parameter-file
print -r -- $vars[MongoInstall]

In bash, maybe you intended to write:

while IFS=: read -r f1 f2
do
  if [ -n "$f1" ] && [ -n "$f2" ]; then
    eval "$f1=\"$f2\""
  fi
done < parameter-file

That assumes the lines of parameter-file doesn't contain "s and doesn't end in :. It makes no attempt to check that $f1 contains a valid variable name or that in general <contents-of-f1>="<contents-of-f2>" forms valid bash code.

The f2=eval $f2 in your code doesn't make sense, that attempts to run the command stored in $f2 (subject to split+glob as not quoted) with f2=eval in its environment.

exporting the variables also serve no purpose other than polluting the environment of commands you'll run later.

Leaving parameter expansions unquoted is also incorrect in bash, especially with [ -z ([ ! -z x ] is [ -n x ] btw).

6
  • Thank you but unfortunately zsh is not an option. I run on bash 4.2.46 and I keep strictly bash approach so ... no sed, no awk, no perl...whatever. No external commands allowed. Commented Aug 5, 2022 at 14:17
  • 1
    @user12877516, you know a shell is a command line interpreter, a command whose whole purpose is to run other commands, right? Commented Aug 5, 2022 at 14:21
  • 1
    If you want "strictly no external commands" then zsh is a much better option @user12877516, since it includes many more facilities than bash which reduce the need for external commands. Commented Aug 5, 2022 at 14:27
  • Thank you Martin but this is not an option. And bash is great if we spend some time to get it. To be complete, I am surprised it doesn't work and I don't understand why. Commented Aug 5, 2022 at 14:37
  • @user12877516, also note that export is to flag a shell variable so it's passed in the environment of external commands that are executed. If you're not going to execute any command, exportings those variables is useless. Commented Aug 5, 2022 at 14:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .