ネストされたループを使用してシェルスクリプトを終了する

ネストされたループを使用してシェルスクリプトを終了する

ネストされたループを持つシェルスクリプトがあり、「終了」が実際にスクリプトを終了せずに現在のループのみを終了することがわかりました。いくつかのエラー状況でスクリプトを完全に終了する他の方法はありますか?

許容可能なエラーがあり、あまりにも多くの書き換えが必要なので、「set -e」を使用したくありません。

今、私はプロセスを手動で終了するためにkillを使用していますが、これを行うより良い方法があるはずです。

答え1

あなたの問題はネストされたループ自体ではありません。これは内部ループがある場所です。サブシェルで実行

これは働きます:

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        for j in $(seq 1 10) ; do
                echo j $j
                sleep 1
                [[ $j = 3 ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done
echo "After all the loops."

出力:

i 1
j 1
j 2
j 3
I've had enough!

これにより、説明する問題が発生します。

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        echo "After the j loop."
done    
echo "After all the loops."

出力:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 2
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
After the j loop.
i 3
LINE root:x:0:0:root:/root:/bin/bash
(...etc...)

解決策は次のとおりです。サブシェルで実行される内部ループの戻り値をテストする必要があります。

#!/bin/bash

for i in $(seq 1 100); do
        echo i $i
        cat /etc/passwd | while read line; do
                echo LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        err=$?; [[ $err != 0 ]] && exit $err
        echo "After the j loop."
done
echo "After all the loops."

テストに注意してください:[[ $? != 0 ]] && exit $?

出力:

i 1
LINE root:x:0:0:root:/root:/bin/bash
LINE bin:x:1:1:bin:/bin:/sbin/nologin
LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!

編集:現在のシェルがどのサブシェルにあるかを確認するには、「応答」スクリプトを変更して、現在のシェルのプロセスIDが何であるかを教えてください。注:これはbash 4でのみ機能します。

#!/bin/bash

for i in $(seq 1 100); do
        echo pid $BASHPID i $i
        cat /etc/passwd | while read line; do
                echo pid $BASHPID LINE $line
                sleep 1
                [[ "$line" = "daemon:x:2:2:daemon:/sbin:/sbin/nologin" ]] && { echo "I've had enough!" 1>&2; exit 1; }
        done
        err=$?; [[ $err != 0 ]] && echo pid $BASHPID && exit $err
        echo "After the j loop."
done
echo "After all the loops."

出力:

pid 31793 i 1
pid 31796 LINE root:x:0:0:root:/root:/bin/bash
pid 31796 LINE bin:x:1:1:bin:/bin:/sbin/nologin
pid 31796 LINE daemon:x:2:2:daemon:/sbin:/sbin/nologin
I've had enough!
pid 31793

変数「i」と「j」はFortranから提供されます。良い一日をお過ごしください。 :-)

答え2

以前の回答では使用を提案しましたが、[[ $? != 0 ]] && exit $?そうではありません。かなり[[ $? != 0 ]]テストはゼロにリセットされるため、期待どおりに機能します$?。つまり、スクリプトが期待どおりに早く終了しても(予想とは異なり)、常にコード0で終了します。また、文字列比較テスト-neではなく数値比較テストを使用することをお勧めします!=。したがって、IMHOのより良い解決策は、次のものを使用することです。

err=$?; [[ $err -ne 0 ]] && exit $err

これが保証されるから実際終了コードが正しく設定されました。

答え3

あなたはそれを使用することができますbreak

からhelp break

Exit a FOR, WHILE or UNTIL loop.  If N is specified, break N enclosing loops.

したがって、3つの閉じたループを終了するには、つまり、基本ループに2つの入れ子になったループがある場合は、次のようにしてすべてのループを終了します。

break 3

答え4

パイプラインの終了ステータスコード(許可された回答に示されている)をテストするのではなく、プロセス置換を使用することです。

だからこれの代わりに:

#!/usr/bin/env bash


seq 3 | while read line_1; do
    seq 3 | while read line_2; do
        if [ "$line_2" -eq 3 ]; then
            echo 'Stop!'
            exit 1
        fi

        echo "$line_1 $line_2"
    done
done

これを行う:

#!/usr/bin/env bash


while read line_1; do
    while read line_2; do
        if [ "$line_2" -eq 3 ]; then
            echo 'Stop!'
            exit 1
        fi

        echo "$line_1 $line_2"
    done < <(seq 3)
done < <(seq 3)

この解決策を見つけましたここ。この回答には、いくつかの重要な警告も記載されています。

この<( )設定(「プロセスの置き換え」)は、すべてのシェルでは使用できず、sh互換モードのbashでも使用できません(つまり、shorと呼ばれる場合/bin/sh)。したがって、スクリプトで明示的なbash shebang(たとえば、#!/bin/bashまたは)を使用し、コマンドでスクリプトを実行してオーバーライドしないでください。#!/usr/bin/env bashsh

関連情報