Linux Bash スクリプトで eval を使用する方法
公開: 2022-08-22
すべての Bash コマンドの中で、古い古いevalの評判はおそらく最悪です。 正当化されたのか、それとも単に悪い報道なのか? この最も人気のない Linux コマンドの使用法と危険性について説明します。
eval について話す必要がある
不注意に使用すると、 evalは予期しない動作やシステムの不安定性につながる可能性があります。 音からして、多分使わない方がいいですよね? そうではありません。
自動車についても同様のことが言えます。 悪者の手に渡れば、彼らは致命的な武器になります。 人々はそれらを突撃や逃走車として使用します。 私たちは皆、車の使用をやめるべきですか? いいえ、もちろん違います。 しかし、それらは適切に、そしてそれらを運転する方法を知っている人によって使用されなければなりません.
evalに適用される通常の形容詞は「悪」です。 しかし、それはすべてそれがどのように使用されているかにかかっています。 evalコマンドは、1 つ以上の変数の値を照合します。 コマンド文字列を作成します。 次に、そのコマンドを実行します。 これは、スクリプトの実行中にコマンドの内容が動的に派生する状況に対処する必要がある場合に役立ちます。
スクリプトの外部から受け取った文字列に対してevalを使用するようにスクリプトを作成すると、問題が発生します。 ユーザーが入力したり、API を介して送信したり、HTTPS 要求にタグ付けしたり、スクリプトの外部にある場所でタグ付けしたりすることができます。
evalが動作する文字列がローカルおよびプログラムによって生成されたものではない場合、文字列に悪意のある命令やその他の不適切な形式の入力が含まれている可能性があります。 明らかに、 evalが悪意のあるコマンドを実行することは望ましくありません。 したがって、安全のために、外部で生成された文字列またはユーザー入力でevalを使用しないでください。
eval の最初のステップ
evalコマンドは、組み込みの Bash シェル コマンドです。 Bash が存在する場合、 evalが存在します。
evalはそのパラメータを単一の文字列に連結します。 連結された要素を区切るために単一のスペースを使用します。 引数を評価し、文字列全体をシェルに渡して実行します。
wordcountという変数を作成しましょう。
wordcount="wc -w raw-notes.md"
文字列変数には、「raw-notes.md」というファイル内の単語をカウントするコマンドが含まれています。
evalを使用して、変数の値を渡すことでそのコマンドを実行できます。
eval " $wordcount " 
コマンドは、サブシェルではなく、現在のシェルで実行されます。 これは簡単に示すことができます。 「variables.txt」という短いテキスト ファイルがあります。 この 2 行が含まれています。
first=ハウツー 2番目=オタク
catを使用して、これらの行をターミナル ウィンドウに送信します。 次に、 evalを使用してcatコマンドを評価し、テキスト ファイル内の命令が実行されるようにします。 これで変数が設定されます。
猫変数.txt eval "$(cat variables.txt)" $first $second をエコー

echoを使用して変数の値を出力すると、 evalコマンドがサブシェルではなく現在のシェルで実行されることがわかります。
サブシェル内のプロセスは、親のシェル環境を変更できません。 eval は現在のシェルで実行されるため、 evalによって設定された変数は、 evalコマンドを起動したシェルから使用できます。
スクリプトでevalを使用する場合、 evalによって変更されるシェルは、スクリプトを起動したシェルではなく、スクリプトが実行されているサブシェルであることに注意してください。
関連: Linux の cat および tac コマンドの使用方法
コマンド文字列での変数の使用
コマンド文字列に他の変数を含めることができます。 整数を保持する 2 つの変数を設定します。
数値1=10 数値2=7
2 つの数値の合計を返すexprコマンドを保持する変数を作成します。 これは、コマンドで 2 つの整数変数の値にアクセスする必要があることを意味します。 exprステートメントの前後のバッククォートに注意してください。
add="`expr $num1 + $num2`"
exprステートメントの結果を表示する別のコマンドを作成します。
show="エコー"
echo文字列の末尾にもexpr文字列の先頭にもスペースを含める必要はないことに注意してください。 evalがそれを処理します。
コマンド全体を実行するには、次のコマンドを使用します。
評価 $表示 $追加

expr文字列内の変数値は、シェルに渡されて実行される前に、 evalによって文字列に置き換えられます。
関連: Bash で変数を操作する方法
変数内の変数へのアクセス
変数に値を割り当ててから、その変数の名前を別の変数に割り当てることができます。 evalを使用すると、2 番目の変数に格納されている値である名前から、最初の変数に保持されている値にアクセスできます。 例は、それを解くのに役立ちます。
このスクリプトをエディターにコピーし、「assign.sh」という名前のファイルとして保存します。
#!/ビン/バッシュ
title="ハウツーオタク"
ウェブページ=タイトル
コマンド="エコー"
eval $command \${$webpage} chmodコマンドで実行可能にする必要があります。
chmod +x assign.sh


この記事からコピーするすべてのスクリプトに対して、これを行う必要があります。 それぞれの場合に適切なスクリプト名を使用してください。
スクリプトを実行すると、 evalコマンドが変数webpageを使用しているにもかかわらず、変数titleからのテキストが表示されます。
./assign.sh

エスケープされたドル記号「 $ 」と中括弧「 {} 」により、 eval は、名前がwebpage変数に格納されている変数内に保持されている値を調べます。
動的に作成された変数の使用
evalを使用して変数を動的に作成できます。 このスクリプトは「loop.sh」と呼ばれます。
#!/ビン/バッシュ
合計=0
label="ループ完了。合計:"
{1..10} の n について
行う
評価 x$n=$n
echo "ループ" $x$n
((合計+=$x$n))
終わり
echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10
エコー $ラベル $合計作成した変数の値のtotalを保持する total という変数を作成します。 次に、 labelという文字列変数を作成します。 これは単純なテキスト文字列です。
10 回ループし、 x1からx10までの 10 個の変数を作成します。 ループ本体のevalステートメントは「x」を提供し、ループ カウンター$nの値を取得して変数名を作成します。 同時に、新しい変数をループ カウンター$nの値に設定します。
新しい変数を端末ウィンドウに出力し、 total変数を新しい変数の値でインクリメントします。
ループの外側では、10 個の新しい変数がすべて 1 行にもう一度表示されます。 計算された名前または派生した名前を使用せずに、実際の名前でも変数を参照できることに注意してください。
最後に、 total変数の値を出力します。
./loop.sh

関連:入門書: Bash ループ: for、while、until
配列での eval の使用
長時間実行され、何らかの処理を実行するスクリプトがあるシナリオを想像してください。 タイム スタンプから作成された名前でログ ファイルに書き込みます。 場合によっては、新しいログ ファイルが開始されます。 スクリプトが終了すると、エラーがなければ、作成したログ ファイルが削除されます。
単純にrm *.logにするのではなく、作成したログ ファイルを削除するだけです。 このスクリプトは、その機能をシミュレートします。 これが「clear-logs.sh」です。
#!/ビン/バッシュ
宣言 -a ログファイル
ファイル数=0
rm_string="エコー"
関数 create_logfile() {
((++ファイル数))
filename=$(date +"%Y-%m-%d_%H-%M-%S").log
logfiles[$filecount]=$ファイル名
echo $filecount "Created" ${logfiles[$filecount]}
}
# スクリプトの本体。 ここでいくつかの処理が行われます
# 定期的にログ ファイルを生成します。 それをシミュレートします
create_logfile
睡眠 3
create_logfile
睡眠 3
create_logfile
睡眠 3
create_logfile
# 削除するファイルはありますか?
for ((file=1; file<=$filecount; file++))
行う
# ログファイルを削除
eval $rm_string ${logfiles[$file]} "削除されました..."
ログファイル[$ファイル]=""
終わりこのスクリプトは、 logfilesという配列を宣言します。 これにより、スクリプトによって作成されたログ ファイルの名前が保持されます。 filecountという変数を宣言します。 これは、作成されたログ ファイルの数を保持します。
また、 rm_stringという文字列も宣言します。 実際のスクリプトでは、これにはrmコマンドが含まれますが、 echoを使用しているため、非破壊的な方法で原理を示すことができます。
関数create_logfile()は、各ログ ファイルの名前と、それが開かれる場所です。 filenameを作成しているだけで、ファイル システムで作成されたふりをしています。
この関数はfilecount変数をインクリメントします。 その初期値はゼロであるため、作成する最初のファイル名は配列の位置 1 に格納されます。 これは意図的に行われますが、後で参照してください。
ファイル名は、 dateコマンドと「.log」拡張子を使用して作成されます。 名前は、配列内のfilecountで示される位置に格納されます。 名前は端末ウィンドウに出力されます。 実際のスクリプトでは、実際のファイルも作成します。
スクリプトの本体は、 sleepコマンドを使用してシミュレートされます。 最初のログ ファイルを作成し、3 秒待ってから別のログ ファイルを作成します。 ファイル名のタイムスタンプが異なるように間隔をあけて 4 つのログ ファイルを作成します。
最後に、ログ ファイルを削除するループがあります。 ループ カウンター ファイルは 1 に設定されます。 作成されたファイルの数を保持するfilecountの値までカウントします。
ログ ファイルが作成されていないためにfilecount 0 に設定されている場合、1 が 0 以下ではないため、ループ本体は実行されません。 これが、宣言時にfilecount変数がゼロに設定され、最初のファイルが作成される前に増分された理由です。
ループ内では、非破壊的なrm_stringと配列から取得したファイルの名前でevalを使用します。 次に、配列要素を空の文字列に設定します。
これは、スクリプトを実行したときに表示されるものです。
./clear-logs.sh

すべてが悪いわけではない
非常に悪意のあるevalには間違いなく用途があります。 ほとんどのツールと同様に、無謀に使用することは危険であり、さまざまな点で危険です。
それが機能する文字列が内部で作成され、人間、API、または HTTPS 要求などからキャプチャされていないことを確認すると、大きな落とし穴を回避できます。
関連: Linuxターミナルで日付と時刻を表示する方法(およびBashスクリプトで使用する方法)



