シェルスクリプトは構文エラーにもかかわらず0exit_statusを返します。

シェルスクリプトは構文エラーにもかかわらず0exit_statusを返します。

次のスクリプトを考えてみましょう。

#!/bin/sh

foo=1
if [[ ! -z $foo ]]; then
    echo abc
fi

Bash構文[[...]]を使用し、Ubuntuでデフォルトのシェル(ダッシュ)を使用して実行すると、期待どおりに機能しません。しかし、戻りコードはまだゼロです。

$ ./tmp.sh
./tmp.sh: 4: ./tmp.sh: [[: not found
$ echo $?
0

終了コードが利用できない場合、スクリプトはこれらのエラーをどのように検出できますか?

答え1

まず、なぜこれが起こるのかを説明します。POSIX シェルコマンド言語仕様 説明する:

ifコマンドの終了状態は、実行されたthenまたはelse複合リストの終了状態でなければならず、実行されていない場合は0でなければなりません。

あなたの場合、その部分は実行されず、終了ステータス0がthenないためです。elseBashを使用してこのスクリプトを実行すると、次のようにゼロになりますman bash

   if list; then list; [ elif list; then list; ] ... [ else list; ] fi

          The if list is executed.  If its exit status is zero,
          the then list is executed.  Otherwise, each elif list is
          executed in turn, and if its exit status is zero, the
          corresponding then list is executed and the command
          completes.  Otherwise, the else list is executed, if
          present.  The exit status is the exit sta‐ tus of the
          last command executed, or zero if no condition tested
          true.

終了コードが利用できない場合、スクリプトはこれらのエラーをどのように検出できますか?

私が考えることができる2つの方法があります。

  • スクリプトを変更できる場合は、elseif構文にいくつか追加してください。

      #!/bin/sh
    
      foo=1
      if [[ ! -z $foo ]]; then
          echo abc
      else
          echo not true
          exit 1
      fi
    
  • 誰かからifを受け取ったが修正したくない場合は、shパターンでshellcheck静的アナライザを使用してコードで可能なバグを見つけて、作成者に報告してください。

      $ shellcheck -s sh dash-exit-status.sh
    
      In dash-exit-status.sh line 4:
      if [[ ! -z $foo ]]; then
         ^-------------^ SC2039: In POSIX sh, [[ ]] is undefined.
            ^-- SC2236: Use -n instead of ! -z.
    
      For more information:
        https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, [[ ]] is undefined.
        https://www.shellcheck.net/wiki/SC2236 -- Use -n instead of ! -z.
    

基本的にこれ抜け穴私にとっては、実行しなければならないスクリプトでPOSIX以外の関数を使用してはいけません/bin/sh

答え2

@muruが述べたように、これはDashに関する限り構文エラーではありません。 are と同様に、は[シェルの特殊文字ではありません。たとえば、面白い名前の一般的なコマンドです。同様のキーワードが Bash/Ksh/Zsh で使用され、キーワードはシェル構文の一部ですが、;([echo[[ifいいえこれをサポートすると、[[他のコマンドとして扱われます。

[[Dashはこれをサポートしていないため、PATH検索は失敗します。 〜のようにArkadiusz Drabczykからの返信ここで、結果は終了ステータス0です。これが実際の構文エラーの場合、シェルはゼロ以外の状態で即座に終了します。

終了コードが利用できない場合、スクリプトはこれらのエラーをどのように検出できますか?

まあ、技術的には…

#!/bin/dash
[[ $1 == "foo" ]]
case $? in
    0)    echo "the first argument was 'foo'";;
    1)    echo "the first argument was something else";;
    127)  echo "[[ not supported";;
    *)    echo "there was some other error";;
esac

または同じで、一連のret=$?withif-elif条件がこれに反対します$ret。これは少し厄介で最善の解決策ではありませんが、特定のコマンドに対して異なるエラーコードを区別する必要がある場合は、そうする必要があります。

目標がDash対Bashを検出することである場合は、スクリプトの先頭に追加のテストを追加して機能していることを確認できます[[

#!/bin/bash
if ! [[ a == a ]] 2>/dev/null; then
    echo "[[ not supported, exiting." >&2
    exit 1
fi

# ...

同様に、使用している他のすべての非POSIX機能のテストを追加できますが、テストしてみてください。みんなもちろん少しぎこちないかもしれません。

同じ機能を持つシェル(KshとZshは一部と互換性がありますが、すべてではありません)ではなく、Bashを正しく要求するには、スクリプトの先頭$BASH_VERSIONで次の点を確認してください。

#!/bin/bash
if [ -z "$BASH_VERSION" ]; then
    echo "Running only with Bash supported, exiting" >&2
    exit 1
fi

# ...

(もちろん、変数を手動で設定することも可能ですが、ユーザーが何をしているのかを知るのが最善です。)

関連情報