1

How can I achieve the following with Bash:

x="search='something something' replace='something' subject=/path/path"

eval "$x"

echo $search
# prints something
# etc.

# (I am trying to do something here)
# So the following wouldn't print **something** anymore

echo $search

I basically want to unset the variables that were set using eval initially.

Adam
  • 2,948
  • 10
  • 43
  • 74
  • Fork a subshell, do `eval` in the subshell and once you're done, exit the subshell – anubhava Dec 04 '17 at 20:06
  • @anubhava not sure how to do any of that. – Adam Dec 04 '17 at 20:08
  • After `eval`, there's nothing special about the variables. Just use `unset search replace subject`. – chepner Dec 04 '17 at 20:40
  • @chepner There are hundreds of them (not sure which will be available) – Adam Dec 04 '17 at 20:46
  • That's one reason using `eval` like this is a bad idea. A script shouldn't need to know the *value* of its variables, but it should know which ones are defined. Where is `x` coming from? – chepner Dec 04 '17 at 20:48

2 Answers2

3

You can fork a subshell, do eval in the subshell and once you're done, exit the subshell:

# do this in a subshell
(
x="search='something' replace='something' subject=/path"
eval "$x"
echo "search=$search"
)

# now out of subshell
echo "search=$search"
anubhava
  • 761,203
  • 64
  • 569
  • 643
3

Preamble: Care using eval as eval is evil...
Whatever...

could do this very quickly:

unset ${x//=*([^ ])}

As pointed out by glenn jackman, this work only if there are no spaces in content of variables.

In this case, we could (very quickly too) prepare some intermediary variable (x without spaces):

xwos=${x//\"*([^\"])\"}        # This will drop `search="don't know"`
xwos=${xwos//\'*([^\'])\'}     # This will drop `search='blah blah'`
unset ${xwos//=*([^ ])}

In fine, adding this to your script will do the job:

xwos=${x//\"*([^\"])\"}; xwos=${xwos//\'*([^\'])\'}; unset ${xwos//=*([^ ])}

Explanation / showcase

Of course, you have to use extglob 's option, if not already set:

shopt -s extglob

Let's try:

x="search='something something' replace='something' subject=/path/path"

eval "$x"

echo $search 
something something

declare -p search replace subject
declare -- search="something something"
declare -- replace="something"
declare -- subject="/path/path"

shopt -s extglob
xwos=${x//\"*([^\"])\"}
printf 'X w/o double quoted spaces 1st:\n    "%s"\n' "$xwos"
X w/o double quoted spaces 1st:
    "search='something something' replace='something' subject=/path/path"

xwos=${xwos//\'*([^\'])\'}
printf 'X w/o single quoted spaces 2nd:\n    "%s"\n' "$xwos"
X w/o single quoted spaces 2nd:
    "search= replace= subject=/path/path"

printf 'String containing variables w/o values:\n    "%s"\n' "${xwos//=*([^ ])}"
String containing variables w/o values:
    "search replace subject"

unset ${xwos//=*([^ ])}

declare -p search replace subject
bash: declare: search: not found
bash: declare: replace: not found
bash: declare: subject: not found

Nota: If you don't need x anymore, you could redefine x instead of populating a new variable:

shopt -s extglob;x=${x//\"*([^\"])\"};x=${x//\'*([^\'])\'};unset ${x//=*([^ ])}

Demo

x="search='something something' replace='something' subject=/path/path"

eval "$x"

echo $search 
something something

shopt -s extglob;x=${x//\"*([^\"])\"};x=${x//\'*([^\'])\'};unset ${x//=*([^ ])}
echo $search
 

 

F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137