What is wrong with this indirect command when run with eval ?
#!/bin/bash
OS=AIX
host=myhost
CMD_AIX="(o=\`host \"$host\" \`)"
CMD=\$CMD_$OS
echo $CMD
eval echo $CMD
eval "$CMD"
Ouput:
$ myscript.sh
$CMD_AIX
(o=`host "myhost" `)
myscript.sh: line 12: (o=`host: command not found
EDIT - NOTES: It's in a very heterogeneous structure with about 40 servers, mixing SCO, AIX and Linux, several versions of each, including variations of 32 and 64 bits. SCO and AIX cannot be updated. Bash and Korn shell are common in all servers but I'm limited to version 3 of bash (in SCO), Ksh has several differences between them, so we're preferring bash, which haven't support to associative arrays in v3 (it cannot be updated and it's outside of my decisions, and these servers will begin to be replaced by AIX 7 and Linux in a few months).
There are a big system to be maintained which includes some scripts already based in sh and bsh, we won't and cannot reconstruct it now, just tittle maintenance to resist some months until the end of replacement.
The approach used in these scripts is largely spread in a big solution which generates some scripts, which we cannot change it from scratch.
The sample code above is just an adopted snippet, it isn't the real code. Please don't consider the logic, just the problem of indirection (eval).
SOLUTION: I got a solution that worked very fine just chaining the eval, like:
torun=`eval $CMD`
output=`eval torun`
This answer and that another one, which worked very fine and answered the question, were oddly down voted a lot.
5 Answers 5
I think the problem with what you're doing there is that it doesn't make any sense. No offense, but I mean that I think you just have the wrong idea about what eval
does. It looks like you're trying to use it just to run a command - that's basic shell functionality and doesn't require that the shell attempt to interpret its last expansions as new commands - which is what eval
does.
The way you have all of your quotes written in you're protecting anything from being secondly evaluated anyway. Or, rather, you're protecting the first pass completely - and so nothing at all happens until the second pass. The problem with all of that is that you might as well just not do a second evaluation (and a second layer of quotes) at all.
It looks to me like you're attempting to run a certain command based on the value of $OS
- maybe...? Well, this is among the most accessible API features in shell.
For example:
OS=AIX
host=myhost
_cmd_AIX() { host "$host"; }
_cmd_WIN() { exit "$((LOSE=1))"; }
You see - you can run those defined functions quoted. Their command name doesn't need any special second interpreter to have significance - not even when it occurs as the result of an expansion. It just needs to be in command position. They will still even accept parameters that way - so you could pass $host
as 1ドル
if you wished. You just call it like this:
"_cmd_$OS"
The $OS
var will expand to AIX
and the resulting command word will be _cmd_AIX
- which is a predefined shell function the name of which occurs as a whole and properly delimited shell word in command position - and so it is executed. Just like that. No contortions necessary - and it even comes with its own array.
Redefine $OS
at any time to the name of some other valid suffix for other behaviors.
So you have a variable CMD
whose value is the string $CMD_AIX
, and the value of the variable CMD_AIX
is the string (o=`host "myhost" `)
. You want to interpret the value of CMD_AIX
as a shell command and execute it.
To execute the value of CMD_AIX
as a shell command, you need to construct a shell command that does that. That's what eval
is for:
eval "$CMD_AIX"
This command, in turn, is constructed dynamically. So you need to run it with eval
:
dispatcher=...
eval "$dispatcher"
Given the way the inner command is constructed, we get
dispatcher="eval \"\$CMD_$OS\""
eval "$dispatcher"
Or cutting one variable out:
eval "eval \"\$CMD_$OS\""
Note that the command (o=`host "myhost" `)
is pointless: it assigns the output of host
to the variable o
in a subshell; the value is not made available outside the subshell. Remove the parentheses. Also, host \"$host\"
is weird: you're putting the value of the variable host
between double quotes; it doesn't matter since host names don't contain shell special characters, but to use the value of the variable when evaluating the shell snippet, you should leave host
unexpanded here.
#!/bin/sh
OS=AIX # should probably be OS=`uname -r`
host=myhost
CMD_AIX="o=\`host \"\$host\"\`"
eval "eval \"\$CMD_$OS\""
If you have ksh93 or bash ≥ 4.0, you can simplify this script by using associative arrays. Instead of stuffing the code for each operating system in a different variable, put them in an associative array.
#!/usr/bin/env bash
typeset -A commands
commands[AIX]="o=\`host \"\$host\"\`"
OS=AIX
host=myhost
if [ -n "${commands[$OS]}" ]; then
eval "${commands[$OS]}"
else
echo >&2 "Operating system $OS not supported"
exit 2
fi
-
Thanks, good explanation. Although it was already fixed, I added notes to the question. I guess this can explain better my situation.Luciano– Luciano2015年06月12日 13:07:54 +00:00Commented Jun 12, 2015 at 13:07
With zsh
:
$ echo $CMD
$CMD_AIX
$ echo ${(e)CMD}
(o=`host "myhost" `)
$ echo ${(e)${(e)CMD}}
(o=myhost has address 1.2.3.4)
The (e)
expansion flag is to evaluate the expansions in the value of the variable being expanded.
With ksh93:
$ function CMD.get { eval ".sh.value=\"$_\""; }
$ function CMD_AIX.get { eval ".sh.value=\"$_\""; }
$ echo "$CMD_AIX"
(o=myhost has address 1.2.3.4)
$ echo "$CMD"
(o=myhost has address 1.2.3.4)
$ CMD=\$USER
$ echo "$CMD"
stephane
CMD.get
is a discipline function that is invoked any time $CMD
is expanded typically used to define what that expansion should be to allow variables with dynamic content. Here we define it as returning the evaluation of its content within double quotes (that assumes the value doesn't contain double quotes).
You could also use types:
typeset -T evaluating_variable=(
function get { eval ".sh.value=\"$_\""; }
)
OS=AIX host=myhost
evaluating_variable CMD_AIX='(o=`host "'$host'"`)'
evaluating_variable CMD=\$CMD_$OS
Now why you'd want to do that in a shell script is another matter.
eval eval $CMD
instead on the last line fixes the problem
This fixed the problem:
eval `eval echo $CMD`
instead of:
eval "$CMD"
You must log in to answer this question.
Explore related questions
See similar questions with these tags.
eval
- and am rather fond of it personally - I can say that it is theeval
code that would give me pause - more likely than any other kind - before I went snipping and sticking.