間接コマンド(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

編集 - 注:SCO、AIX、およびLinuxが混在しており、それぞれ32ビットおよび64ビットのバリエーションを含む、複数のバージョンを持つ約40台のサーバーで構成される非常に異質な構造を持っています。 SCOとAIXは更新できません。 BashとKornシェルはすべてのサーバーで共通ですが、bashバージョン3(SCOでは)に制限されており、Kshにはいくつかの違いがあるため、v3で連想配列をサポートしていないbashを好む(更新できません)。これらのサーバーは数ヶ月以内にAIX 7およびLinuxに置き換えられ始めます。

すでにshとbshベースのいくつかのスクリプトを含むメンテナンスが必要な大規模システムがあります。今は再構築せず、再構築することもできません。交換が完了するまで数ヶ月間ヘッダーのメンテナンスを行うだけです。

これらのスクリプトで使用される方法は、一部のスクリプトを生成する大規模なソリューションに主に分散されており、最初から変更することはできません。

上記のサンプルコードは採用されたコードの一部であり、実際のコードではありません。論理について考えるのではなく、間接参照(評価)についてのみ考えてください。

解決策:非常に効果的なソリューションを得ましたリンク評価、よい:

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

これ答えてもう一つ、うまく動作し、質問に答えたが、奇妙なことに多くの反対でした。

答え1

私の考えであなたがしていることの問題は、それが何の意味もないということです。攻撃しようとする意図はありませんが、私のポイントは、あなたが何をしているのかについて間違った考えを持っていると思うということですeval。コマンドを実行するためにそれを使用したいようです。これはデフォルトのシェル機能であり、シェルが最後の拡張を新しいコマンドとして解釈しようとする必要はありません。これがまさにそれですeval

すべての引用を書く方法は、何も評価されないようにすることです。あるいは、むしろ最初のレベルを保護しています。完全- だから、2番目のパスまでは何も起こりません。これらすべての問題は、あなたもそうすることができるということです。いいえ2回目の評価を実施(および2番目のレベルの引用符)別言します。

$OS- 値に基づいていくつかのコマンドを実行したいようです。おそらく…?さて、これはシェルで最もアクセスしやすいAPI機能の1つです。

たとえば、

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

ご覧のとおり、参照されている定義された関数を実行できます。そのコマンド名は、拡張の結果として発生しても意味を持つために特別な2番目のソルバーは必要ありません。ちょうどコマンドの位置にする必要があります。このように引数を受け入れることもできるので、必要に応じて$host引数を渡すことができます。$1ただこう呼んでください。

"_cmd_$OS"

varが$OS展開され、AIX結果のコマンドワードは、_cmd_AIX名前が完全に表示され、コマンドの場所で適切に区切られたシェルワードを持つ事前定義されたシェル関数であるために実行されます。それはすべてです。ねじる必要はありません。独自の配列も付属しています。

$OS異なる動作に対して異なる有効なサフィックスを使用して名前を自由にオーバーライドします。

答え2

CMDしたがって、値がstringの変数があり、$CMD_AIX変数の値CMD_AIXはstringです(o=`host "myhost" `)CMD_AIXシェルコマンドとして解釈して実行したい値です。

値をCMD_AIXシェルコマンドとして実行するには、これを実行するシェルコマンドを設定する必要があります。eval目的は次のとおりです。

eval "$CMD_AIX"

このコマンドは再び動的に構成されます。だからあなたはそれを実行する必要がありますeval

dispatcher=…
eval "$dispatcher"

内部コマンドの構成方法を考えると、次のようになります。

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

または変数を削除します。

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

このコマンドは意味がありません。その出力はサブシェルの変数(o=`host "myhost" `)に割り当てられ、その値はサブシェルの外部では使用できません。ブラケットを取り外します。さらに、奇妙なことは、ホスト名にシェル特殊文字が含まれていないため、変数の値を二重引用符で囲むことは重要ではありませんが、シェルフラグメントを評価するときに変数値を使用するにはそのままにしておく必要があります。拡張状態。hostohost \"$host\"hosthost

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

これでシェルスクリプトでこれを行う理由は別の話です。

答え4

これにより問題が解決しました。

eval `eval echo $CMD`

変える:

eval "$CMD"

関連情報