Linux Bash スクリプトで eval を使用する方法

公開: 2022-08-22
bash プロンプトを表示している Linux ラップトップ
Fatmawati achmad zaenuri/Shutterstock.com

すべての Bash コマンドの中で、古い古いevalの評判はおそらく最悪です。 正当化されたのか、それとも単に悪い報道なのか? この最も人気のない Linux コマンドの使用法と危険性について説明します。

eval について話す必要がある

不注意に使用すると、 evalは予期しない動作やシステムの不安定性につながる可能性があります。 音からして、多分使わない方がいいですよね? そうではありません。

自動車についても同様のことが言えます。 悪者の手に渡れば、彼らは致命的な武器になります。 人々はそれらを突撃や逃走車として使用します。 私たちは皆、車の使用をやめるべきですか? いいえ、もちろん違います。 しかし、それらは適切に、そしてそれらを運転する方法を知っている人によって使用されなければなりません.

Bash で変数を操作する方法
関連Bash で変数を操作する方法

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 "

文字列変数で eval を使用してファイル内の単語をカウントする

コマンドは、サブシェルではなく、現在のシェルで実行されます。 これは簡単に示すことができます。 「variables.txt」という短いテキスト ファイルがあります。 この 2 行が含まれています。

 first=ハウツー
2番目=オタク

catを使用して、これらの行をターミナル ウィンドウに送信します。 次に、 evalを使用してcatコマンドを評価し、テキスト ファイル内の命令が実行されるようにします。 これで変数が設定されます。

 猫変数.txt
eval "$(cat variables.txt)"
$first $second をエコー

現在のシェルで eval によって設定された変数へのアクセス

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 

chmod を使用してスクリプトを実行可能にする

この記事からコピーするすべてのスクリプトに対して、これを行う必要があります。 それぞれの場合に適切なスクリプト名を使用してください。

スクリプトを実行すると、 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の値に設定します。

Linux Bash スクリプトの for ループの 9 つの例
関連9 Linux Bash スクリプトの for ループの例

新しい変数を端末ウィンドウに出力し、 total変数を新しい変数の値でインクリメントします。

ループの外側では、10 個の新しい変数がすべて 1 行にもう一度表示されます。 計算された名前または派生した名前を使用せずに、実際の名前でも変数を参照できることに注意してください。

最後に、 total変数の値を出力します。

 ./loop.sh 

eval を使用して動的に変数を作成する

関連:入門書: 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で示される位置に格納されます。 名前は端末ウィンドウに出力されます。 実際のスクリプトでは、実際のファイルも作成します。

Linux スリープ コマンドを使用して Bash スクリプトを一時停止する方法
関連Linux Sleep コマンドを使用して Bash スクリプトを一時停止する方法

スクリプトの本体は、 sleepコマンドを使用してシミュレートされます。 最初のログ ファイルを作成し、3 秒待ってから別のログ ファイルを作成します。 ファイル名のタイムスタンプが異なるように間隔をあけて 4 つのログ ファイルを作成します。

最後に、ログ ファイルを削除するループがあります。 ループ カウンター ファイルは 1 に設定されます。 作成されたファイルの数を保持するfilecountの値までカウントします。

ログ ファイルが作成されていないためにfilecount 0 に設定されている場合、1 が 0 以下ではないため、ループ本体は実行されません。 これが、宣言時にfilecount変数がゼロに設定され、最初のファイルが作成される前に増分された理由です。

ループ内では、非破壊的なrm_stringと配列から取得したファイルの名前でevalを使用します。 次に、配列要素を空の文字列に設定します。

これは、スクリプトを実行したときに表示されるものです。

 ./clear-logs.sh 

名前が配列に格納されているファイルの削除

すべてが悪いわけではない

非常に悪意のあるevalには間違いなく用途があります。 ほとんどのツールと同様に、無謀に使用することは危険であり、さまざまな点で危険です。

それが機能する文字列が内部で作成され、人間、API、または HTTPS 要求などからキャプチャされていないことを確認すると、大きな落とし穴を回避できます。

関連: Linuxターミナルで日付と時刻を表示する方法(およびBashスクリプトで使用する方法)