使用间接命令(eval)输出到变量

使用间接命令(eval)输出到变量

使用 eval 运行时,此间接命令有什么问题?

#!/bin/bash
OS=AIX
host=myhost

CMD_AIX="(o=\`host \"$host\" \`)"
CMD=\$CMD_$OS

echo $CMD
eval echo $CMD

eval "$CMD"

输出:

$ myscript.sh
$CMD_AIX
(o=`host "myhost" `)
myscript.sh: line 12: (o=`host: command not found

编辑 - 注意:它采用非常异构的结构,约有 40 台服务器,混合了 SCO、AIX 和 Linux,每种都有多个版本,包括 32 位和 64 位的变体。 SCO和AIX无法更新。 Bash 和 Korn shell 在所有服务器中都很常见,但我仅限于 bash 版本 3(在 SCO 中),Ksh 它们之间有一些差异,所以我们更喜欢 bash,它不支持 v3 中的关联数组(它无法更新,这不属于我的决定范围,并且这些服务器将在几个月内开始被 AIX 7 和 Linux 取代)。

有一个很大的系统需要维护,其中包括一些已经基于 sh 和 bsh 的脚本,我们现在不会也无法重建它,只是标题维护以抵抗几个月直到更换结束。

这些脚本中使用的方法很大程度上分布在一个生成一些脚本的大型解决方案中,我们无法从头开始更改它。

上面的示例代码只是一个采用的代码片段,它不是真正的代码。请不要考虑逻辑,只考虑间接(eval)的问题。

解决方案:我得到了一个效果很好的解决方案链接评估, 喜欢:

torun=`eval $CMD`
output=`eval torun`

回答并另一个,它工作得很好并回答了问题,但奇怪的是,它被否决了很多票。

答案1

我认为你在那里所做的事情的问题在于它没有任何意义。无意冒犯,但我的意思是我认为你只是对它的作用有错误的想法eval。看起来您只是尝试使用它来运行命令 - 这是基本的 shell 功能,并不要求 shell 尝试将其最后的扩展解释为新命令 - 事实正是如此eval

你写下所有引言的方式是为了保护任何东西不被二次评估。或者更确切地说,你正在保护第一关完全地- 所以直到第二遍之前什么都不会发生。所有这一切的问题是你不妨不是进行第二次评估(和第二层引号)根本不。

在我看来,您正在尝试根据$OS-的值运行某个命令或许...?嗯,这是 shell 中最容易访问的 API 功能之一。

例如:

OS=AIX
host=myhost
_cmd_AIX() { host "$host"; }
_cmd_WIN() { exit "$((LOSE=1))"; }

你看 - 你可以运行那些引用的定义函数。它们的命令名称不需要任何特殊的第二个解释器就具有意义 - 即使它是作为扩展的结果出现的。它只需要处于指挥位置即可。他们甚至仍然会以这种方式接受参数 - 所以你可以$host按照$1你的意愿传递。你只需这样称呼它:

"_cmd_$OS"

var$OS将扩展为AIX,结果命令字将是_cmd_AIX- 这是一个预定义的 shell 函数,其名称作为一个整体出现,并在命令位置正确分隔 shell 字 - 因此它被执行。就这样。无需扭曲 - 它甚至带有自己的阵列。

$OS随时为其他行为重新定义一些其他有效后缀的名称。

答案2

所以你有一个变量CMD,其值是字符串$CMD_AIX,并且变量的值CMD_AIX是字符串(o=`host "myhost" `)。您想要将 的值解释CMD_AIX为 shell 命令并执行它。

要将 的值CMD_AIX作为 shell 命令执行,您需要构造一个执行此操作的 shell 命令。这就是eval目的:

eval "$CMD_AIX"

该命令又是动态构造的。所以你需要运行它eval

dispatcher=…
eval "$dispatcher"

考虑到内部命令的构造方式,我们得到

dispatcher="eval \"\$CMD_$OS\""
eval "$dispatcher"

或者删除一个变量:

eval "eval \"\$CMD_$OS\""

请注意,该命令(o=`host "myhost" `)毫无意义:它将 的输出分配给子 shell 中的host变量;o该值在子 shell 之外不可用。删除括号。另外,host \"$host\"很奇怪:您将变量的值放在host双引号之间;这并不重要,因为主机名不包含 shell 特殊字符,但要在评估 shell 片段时使用变量的值,您应该host在此处保留未展开状态。

#!/bin/sh
OS=AIX # should probably be OS=`uname -r`
host=myhost
CMD_AIX="o=\`host \"\$host\"\`"
eval "eval \"\$CMD_$OS\""

如果您有 ksh93 或 bash ≥ 4.0,则可以使用关联数组来简化此脚本。不要将每个操作系统的代码填充到不同的变量中,而是将它们放在关联数组

#!/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

答案3

zsh

$ echo $CMD
$CMD_AIX
$ echo ${(e)CMD}
(o=`host "myhost" `)
$ echo ${(e)${(e)CMD}}
(o=myhost has address 1.2.3.4)

扩展(e)标志用于评估正在扩展的变量值的扩展。

使用 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是一个在扩展时随时调用的规则函数,$CMD通常用于定义扩展应该是什么以允许具有动态内容的变量。在这里,我们将其定义为返回双引号内内容的评估(假设该值不包含双引号)。

您还可以使用类型:

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

现在为什么要在 shell 脚本中执行此操作是另一回事。

答案4

这解决了问题:

eval `eval echo $CMD`

代替:

eval "$CMD"

相关内容