[BASH] eval command not expanding variables as expected.

Hi Guys,

I wrote a collection of bash functions years ago and now need to use them again but
I'm getting some error messages when eval tries to expand the variables names.

I recollect that I used the shopt command to set one of the options but I can't quite
remember the command that I used. However, I may be wrong regarding the shopt
command.

Anyway, the function in question is:

function array_load
 {
    local IFS=$'\n'
    eval "$1=( \$( < \"\${$2}\" ) )"
 }

This is the output from the sh -x command:

+ declare -i __ITR__
+ declare -a __FILE_SQL__
+ declare -a __TEMP_SQL__
+ array_load __FILE_SQL__ __SRC__
+ local 'IFS=
'
+ eval '__FILE_SQL__=( $( < "${__SRC__}" ) )'
++ __FILE_SQL__=($( < "${__SRC__}" ))
  /Users/Shared/#BASHLIB/type/array.bash: line 53: ~/djia.csv: No such file or directory

The __SRC__ variable contains a relative reference to a file i.e. ~/djia.csv but I've also
tried using an absolute reference from the root directory which also does not get found.

Any help would be appreciated.
TIA Guys. :wink:

I'd trust the system when it complains a file not residing where it looks for it.
Did you make very sure that file exists?

What happens in your script is that bash 's mapfile / readarray builtin is duplicated by using a "command substitution". man bash :

Above mentioned builtins serve the same purpose, offer some more options, and don't rely on the deprecated, dangerous eval command. man bash :

Why don't you just abandon the function?

you can also read from a file into an associative array

cat file
(
[apple]=123457
[mango]=123456
[orange]=1234567
)
declare -A arr
m=arr
eval $m=$(<file)
echo ${arr[@]}
1234567 123456 123457
echo ${!arr[@]}
orange mango apple

probably

shopt -s extquote

Hi nezabudka...

Remember! Only on bash version 4.0.x and above.
We Apple users and probably others only have version 3.2.x so associative arrays are a none starter.
As the OP has not quoted his bash version nor the OS and HW in use then we can only assume he MIGHT have bash version 4.0.x[plus] available.

Just an observation...

1 Like

Hi.

I think those of us who use macos may need to get used to zsh . Here is a demo zsh script that was run on Mojave, not using eval :

#!/usr/bin/env zsh

# @(#) z1       Demonstrate feature.

set +o nounset
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && zsh $C
set -o nounset

FILE=${1-data1}

pl " Input file $FILE:"
head $FILE

# IFS=$'\n,'; h=($(<file.csv))
# https://unix.stackexchange.com/questions/464626/making-associative-array-based-on-another-associative-array
# Printing advice from:
# http://zsh.sourceforge.net/Guide/zshguide05.html#l122
pl " Results:"
typeset -A a
IFS=$'\n,\t='; a=($(<$FILE))

pe;pe " Plain print:"
print ${a}
pe;pe " Values:"
print ${(v)a}
pe;pe " Keys:"
print ${(k)a}
pe;pe " Both vertically:"
print -l "${(@kv)a}"

exit 0

producing:

$ ./z1

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Apple/BSD, Darwin 18.7.0, x86_64
Distribution        : macOS 10.14.6 (18G103), Mojave
zsh 5.3

-----
 Input file data1:
red=stop
green=go
yellow=caution

-----
 Results:

 Plain print:
caution stop go

 Values:
caution stop go

 Keys:
yellow red green

 Both vertically:
yellow
caution
red
stop
green
go

Best wishes ... cheers, drl

2 Likes

The problem is ~ is not being expanded. It turns out to be a little tricky to expand ~ but also avoid word separation on the path name (white spaces in you array source filename).

Try:

function array_load
{
    local IFS=$'\n'
    local ARR_FILE=$(eval echo ${!2})
    eval "$1=( \$( < \"$ARR_FILE\" ) )"
}
2 Likes

Congrats for finding the root cause! If ~ is not correctly expanded, we have to do this ourselves in its stead. Try

function array_load  {
        local IFS=$'\n'
        eval "$1=( \$( < \"\${$2/\~/$HOME}\" ) )"
 }

RudiC although your replace works for ~/dir/path it would not work for ~root/data/mydatafile or /home/dba/data~files/myarray.dat .
The function in post #7 supplies a more accurate expansion.

1 Like