setコマンド、shoptコマンド

「set」コマンドは Bash スクリプトの重要な部分ですが、見落とされることが多く、スクリプトのセキュリティと保守性に問題が生じます。この章では、より安全な Bash スクリプトを作成するのに役立つ「set」の基本的な使用法を紹介します。

導入

Bash がスクリプトを実行すると、サブシェルが作成されることがわかっています。

$ bash スクリプト.sh

上記のコードでは、script.sh がサブシェルで実行されます。このサブシェルはスクリプトの実行環境であり、Bash はデフォルトでこの環境にさまざまなパラメーターを提供します。

set コマンドは、サブシェル環境の実行パラメータを変更する、つまり環境をカスタマイズするために使用されます。カスタマイズできるパラメーターは多数あります。公式マニュアル には、この章で説明されています。最も一般的に使用されるものはいくつかあります。

ちなみに、コマンドラインでパラメータを指定せずに set を直接実行すると、すべての環境変数とシェル関数が表示されます。

$セット

セット -u

スクリプトの実行時に、存在しない変数が見つかった場合、Bash はデフォルトでそれを無視します。

#!/usr/bin/env bash

$aをエコーする
エコーバー

上記のコードでは、$a は存在しない変数です。実行結果は以下の通りです。

$ bash スクリプト.sh

バー

ご覧のとおり、echo $a は空行を出力しますが、Bash は存在しない $a を無視し、その後 echo bar を実行し続けます。ほとんどの場合、これは開発者が望む動作ではありません。変数が存在しない場合、スクリプトはサイレントに実行されるのではなく、エラーを報告する必要があります。

この動作を変更するには set -u を使用します。スクリプトがそれをヘッダーに追加すると、存在しない変数が見つかったときにエラーが報告され、実行が停止されます。

#!/usr/bin/env bash
セット -u

エコー $a
エコーバー

走行結果は以下の通りです。

$ bash スクリプト.sh
bash:script.sh:4行目:a:バインドされていない変数

ご覧のとおり、スクリプトはエラーを報告し、後続のステートメントは実行されなくなります。

-u-o nounset という別の書き方もあり、この 2 つは同等です。

set -o 名詞セット

セット -x

デフォルトでは、スクリプトの実行後は実行結果のみが出力され、他の内容は出力されません。複数のコマンドを連続して実行すると、実行結果が連続して出力されます。どのコマンドが特定のコンテンツを生成したかが不明瞭な場合があります。

set -x は、結果を実行する前に実行されたコマンドラインを出力するために使用されます。

#!/usr/bin/env bash
セット -x

エコーバー

上記のスクリプトを実行すると、結果は次のようになります。

$ bash スクリプト.sh
+ エコーバー
バー

ご覧のとおり、echo bar を実行する前にコマンドが最初に出力され、行の先頭は + で表されます。これは、複雑なスクリプトをデバッグする場合に役立ちます。

-x -o xtrace と書く方法もあります。

set -o xtrace

スクリプト内のコマンド出力をオフにしたい場合は、set +x を使用できます。

#!/bin/bash

数値=1

セット -x
if [ $number = "1" ];
  エコー「数値は1に等しい」
それ以外
  echo "数値が 1 に等しくありません"
フィ
+x を設定

上記の例では、特定のコードセグメントに対してのみコマンド出力がオンになります。

Bash エラー処理

スクリプト内に実行に失敗したコマンドがある場合 (戻り値が「0」でない場合)、Bash はデフォルトで後続のコマンドを実行し続けます。

#!/usr/bin/env bash

ふー
エコーバー

上記のスクリプトでは、「foo」は存在しないコマンドなので、実行するとエラーが報告されます。ただし、Bash はこのエラーを無視して実行を続行します。

$ bash スクリプト.sh
script.sh: 行 3: foo: コマンドが見つかりません
バー

ご覧のとおり、Bash はエラーを表示するだけで、実行は終了しません。

この動作は、スクリプトのセキュリティとデバッグにとって非常に有害です。実際の開発では、コマンドが失敗した場合、エラーが蓄積するのを防ぐためにスクリプトの実行を停止する必要があることがよくあります。このとき、以下のような書き込み方法が一般的です。

コマンド || 終了 1

上記の記述は、「command」の戻り値がゼロ以外である限り、スクリプトは実行を停止することを意味します。

実行を停止する前に複数の操作を完了する必要がある場合は、次の 3 つの書き込み方法を使用する必要があります。

# 書き方その1
コマンド || { エコー "コマンドが失敗しました";

# 書き方2
if ! コマンド; then 「コマンドが失敗しました」を終了します。

# 書き方3
指示
if [ "$?" -ne 0 ]; then "コマンドが失敗しました";

また、実行停止以外にも別の状況があります。 2 つのコマンドに継承関係があり、最初のコマンドが成功した場合にのみ 2 番目のコマンドを実行できる場合は、次の記述方法を使用する必要があります。

コマンド1 && コマンド2

セット -e

上記の書き方は少々面倒で見落としがちです。 set -e はこの問題を根本的に解決します。これにより、エラーが発生する限りスクリプトの実行が終了します。

#!/usr/bin/env bash
セット -e

ふー
エコーバー

実行結果は以下の通りです。

$ bash スクリプト.sh
script.sh: 行 4: foo: コマンドが見つかりません

ご覧のとおり、4 行目の実行が失敗すると、スクリプトは実行を終了します。

set -e は戻り値に基づいてコマンドが失敗したかどうかを判断します。ただし、一部のコマンドからのゼロ以外の戻り値は失敗を示していない場合があり、開発者はコマンドが失敗した場合でもスクリプトの実行を継続したい場合があります。このとき、set -e を一時的に閉じて、コマンドの実行後に再び set -e を開くことができます。

セット+e
コマンド1
コマンド2
セット -e

上記のコードでは、「set +e」は「-e」オプションをオフにすることを意味し、「set -e」は「-e」オプションをオンに戻すことを意味します。

もう 1 つの方法は、コマンドの実行が失敗してもスクリプトの実行が終了しないように command || を使用することです。

#!/bin/bash
セット -e

ふー || 本当
エコーバー

上記のコードで、「true」は、この行のステートメントが常に正常に実行され、次の「echo bar」が実行されることを意味します。

-e -o errexit と書く方法もあります。

set -o errexit

set -o パイプ失敗

「set -e」には例外があり、パイプコマンドには適用されません。

いわゆるパイプライン コマンドとは、複数のサブコマンドがパイプライン演算子 (|) を介して 1 つの大きなコマンドに結合されることを意味します。 Bash は、最後のサブコマンドの戻り値をコマンド全体の戻り値として使用します。つまり、最後のサブコマンドが失敗しない限り、パイプライン コマンドは常に正常に実行されるため、後続のコマンドは引き続き実行され、「set -e」は無効になります。

以下の例を見てください。

#!/usr/bin/env bash
セット -e

フー | エコー
エコーバー

実行結果は以下の通りです。

$ bash スクリプト.sh
ある
script.sh: 行 4: foo: コマンドが見つかりません
バー

上記のコードでは、「foo」は存在しないコマンドですが、パイプライン コマンド「foo | echo a」は正常に実行され、後続の「echo bar」は引き続き実行されます。

この状況を解決するには、「set -o Pipefail」を使用します。1 つのサブコマンドが失敗すると、パイプライン コマンド全体が失敗し、スクリプトの実行が終了します。

#!/usr/bin/env bash
set -eo パイプ失敗

フー | エコー
エコーバー

実行後の結果は以下の通り。

$ bash スクリプト.sh
ある
script.sh: 行 4: foo: コマンドが見つかりません

ご覧のとおり、「echo bar」は実行されません。

セット -E

-e パラメータが設定されると、関数内のエラーは trap コマンドによって捕捉されなくなります (「trap コマンド」の章を参照)。 -E パラメータはこの動作を修正し、関数が trap コマンドも継承できるようにします。

#!/bin/bash
セット -e

トラップ「エコー ERR トラップが起動されました!」

myfunc()
{
  # 'foo' は存在しないコマンドです
  ふー
}

マイファンク

上記の例では、「myfunc」関数が存在しないコマンド「foo」を内部で呼び出しているため、この関数の実行時にエラーが報告されます。

$ bash テスト.sh
test.sh: 9行目: foo: コマンドが見つかりません

ただし、set -e の設定により、関数内で報告されるエラーは trap コマンドによって捕捉されないため、-E パラメータを追加する必要があります。

#!/bin/bash
set -Eeuo パイプ失敗

トラップ「エコー ERR トラップが起動されました!」

myfunc()
{
  # 'foo' は存在しないコマンドです
  ふー
}

マイファンク

上記のスクリプトを実行すると、「trap」コマンドが有効になっていることがわかります。

$ bash テスト.sh
test.sh: 9行目: foo: コマンドが見つかりません
ERRトラップが発動しました!

その他のパラメータ

set コマンドにはいくつかの追加パラメータがあります。

  • set -n: set -o noexec と同等で、コマンドを実行せず、構文が正しいかどうかのみをチェックします。
  • set -f: set -o noglob と同等。これは、ワイルドカード文字のファイル名拡張を行わないことを意味します。
  • set -v: set -o verbose と同等です。これは、シェルが受け取った入力の各行を出力することを意味します。
  • set -o noclobber: リダイレクト演算子 > を使用して既存のファイルを上書きするのを防ぎます。

上記の -f および -v パラメータは、それぞれ set +f および set +v を使用してオフにできます。

set コマンドの概要

上記で強調表示した「set」コマンドのパラメータは、通常、一緒に使用されます。

# 書き方その1
set -Eeuxo パイプ失敗

# 書き方2
セト・ウー
set -o パイプ失敗

これら 2 つの記述方法は、すべての Bash スクリプトの先頭に配置することをお勧めします。

もう 1 つの方法は、Bash スクリプトの実行時にコマンド ラインからこれらのパラメーターを渡すことです。

$ bash -euxo パイプ失敗スクリプト.sh

ショップコマンド

shopt コマンドは、シェル パラメータを調整するために使用されます。これは、set コマンドとよく似ています。これら 2 つの類似したコマンドの主な理由は、set が Ksh から継承され、POSIX 仕様の一部であるのに対し、shopt は Bash 固有であるためです。

「shopt」を直接入力すると、すべてのパラメータとそれぞれのオン/オフ状態が表示されます。

$ショップ

shopt コマンドの後にパラメータ名が続き、パラメータがオンになっているかどうかを確認できます。

$ショップグロブスター
グロブスターオフ

上の例は、「globstar」パラメータがデフォルトでオフになっていることを示しています。

(1)-s

-s は特定のパラメータをオンにするために使用されます。

$ shopt -s オプション名はこちら

(2)-u

-u はパラメータをオフにするために使用されます。

$ shopt -u オプション名はこちら

たとえば、パラメータ histappend は、現在のシェルを終了するときに操作履歴を履歴ファイルに追加することを意味します。このパラメータはデフォルトでオンになっています。次のコマンドを使用してオフにすると、現在のシェルの操作履歴によって履歴ファイル全体が置き換えられます。

$ shopt -u histappend

(3)-q

-q の機能も、特定のパラメータがオンになっているかどうかを問い合わせることですが、問い合わせ結果を直接出力するのではなく、コマンド ($?) の実行状態によって表現されます。ステータスが「0」の場合はパラメータがオンであることを意味し、「1」の場合はパラメータがオフであることを意味します。

$ shopt -q グロブスター
$エコー$?
1

上記のコマンドは、「globstar」パラメータがオンになっているかどうかを問い合わせます。戻りステータスは「1」で、パラメータが閉じられていることを示します。

この使用法は主に、「if」条件構造で使用するスクリプトで使用されます。次の例は、このパラメータがオンになっている場合、「if」構造内のステートメントが実行されることを示しています。

if (shopt -q グロブスター);
  ...
フィ

参考リンク


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0