1. 概要

ほとんどのプログラミングおよびスクリプト言語には、実行をスクリプト内の別の場所に移動できるフロー制御ステートメントがあります。 多くの場合、これらにはgotoステートメントが含まれます。

Bashはgotoをサポートしていませんが、フロー制御のための他のいくつかのメカニズムを提供します。

このチュートリアルでは、Bashで goto をシミュレートする方法と、組み込みのフロー制御機能を使用してコードを飛び越えたり、ループから飛び出したり、代替アクションを実行したりする方法を見ていきます。

2. Bashスクリプトでコードを飛び越える

gotoのジャンプ動作をシミュレートすることから始めましょう。

2.1. gotoの基本構文

goto ステートメントは通常、ラベルで識別される行に制御を移します。

    statement1

    goto labe11
    statement2 #skipped
    #... more lines skipped
    statement9 #skipped

label1:
    statement10  #executed

2.2. Bashスクリプトから複数行を読み取る

これをBashでネイティブに行うことはできませんが、オペレーター < スクリプトの行をにリダイレクトします stdin 文字列までここに遭遇しました。 これはしばしば「ヒアドキュメント」と呼ばれます。

2.3. 実例

を使ってこれを試してみましょう猫<

#!/bin/bash

echo "Starting the script"
cat >/dev/null <<LABEL_1
echo "This command is not run"
LABEL_1
echo "And this command is run"
cat >/dev/null <<LABEL_EXIT
echo "This command is not run as the first one"
LABEL_EXIT
echo "The script is done"

次に、それを実行して、出力を見てみましょう。

./cat_goto_example
Starting the script

And this command is run
The script is done

ご覧のとおり、catLABEL_1の間のechoステートメントは実行されませんでした。

2.4. catのコードはどうなりますか

catが何をしているのか見てみましょう。 したがって、 / dev /nullリダイレクトを省略しましょう。

#!/bin/bash

echo "Starting the script"
cat <<LABEL_1
echo "This command is not run"
LABEL_1
echo "And this command is run"
cat <<LABEL_EXIT
echo "This command is not run as the first one"
LABEL_EXIT
echo "The script is done"

これで、catの出力がコンソールに表示されることがわかります。

Starting the script

echo "This command is not run"

And this command is run

echo "This command is not run as the first one"
The script is done

したがって、catおよびheredocアプローチでは、スクリプトの行を読み取ることはできますが、実行することはできません。 goto をシミュレートするこの回避策は、スクリプトを介して以降でのみ機能することに注意してください。

3. 条件付きでBashスクリプトのコードを飛び越える

最も一般的には、スクリプトで goto を何らかの条件と組み合わせて使用します。おそらく、実行したくないステップを飛び越えます。

3.1. 条件付きの作成後藤猫<

この擬似コードに似たロジックを実現しようとしています。

    if condition 
	goto label1
		
    statement1 #skipped if condition is met
    # ...
label1
    statement10 #always executed

上記の手法では、少し余分なトリックを使用してこれを実現できます。

if [ -n "$GOTO_CONDITION" ] ; then
    cat >/dev/null
else
    eval "$(cat -)"
fi <<'LABEL'

ここでは、 if ステートメントの両方のブランチを使用して、必要な処理を実行するcat式を作成しています。 GOTO_CONDITIONtrue の場合に実行される部分は、以前と同様にlabel1までのコード行を削除します。

それどころか、’ そうしないと ‘ 部スクリプトを再構築しますによって提供される行から << 演算子を実行し、全体として実行します。

詳細には、“ $(cat-)” コマンドはスクリプトの行を連結し、結果をbash変数としてevalに提示します。 その結果、スクリプトの各行を互いに別々に実行することを避けます。

さらに、変数の展開を避けるために、ラベル名を引用符で囲んだヒアドキュメント構造を使用する必要があります。

3.2. 条件付きgotoの例

構築を使用するスクリプトを作成しましょう。

#!/bin/bash

n=2
echo "Starting the script with n = $n"
foo="FOO"

if [ $n -eq 2 ] ; then
    cat >/dev/null
else
    eval "$(cat -)"
fi <<'LABEL_1'

echo "This is the foo variable: $foo"
echo "This command is not run when jumped over with goto"
bar="BAR"
echo "This is the bar variable: $bar"
LABEL_1
echo "After LABEL_1"
echo "This is the foo variable: $foo"
echo "This is the bar variable: $bar"
echo "And this command is run"
echo "The script is done"

算術式をチェックして、 gotoの条件がスクリプトの実行中に評価される可能性があることを強調します。 さらに、 foo、 bar、の2つの変数を使用して、それらの可視性を調べます。

3.3. 条件付きgotoのテスト

goto がアクティブな場合の結果を確認しましょう– n = 2の場合:

Starting the script with n = 2
After LABEL_1
This is the foo variable: FOO
This is the bar variable: 
And this command is run
The script is done

今回は、コードを飛び越えました。 さらに、変数fooは定義されていますが、barは定義されていません

ここで、 goto が実行されていないときに、 n =10でスクリプトを開始しましょう。

Starting the script with n = 10
This is the foo variable: FOO
This command is not run when jumped over with goto
This is the bar variable: BAR
After LABEL_1
This is the foo variable: FOO
This is the bar variable: BAR
And this command is run
The script is done

ここでは、すべてのスクリプトが実行されたことがわかります。 さらに、両方の変数が適切に定義され、表示されます

最後に、ローカル変数foobarを正しく使用すると、スクリプトが1つの統合された全体として実行されたことがわかります。

4. コードの一部を有効または無効にするためのBashの代替

スクリプトの開発またはデバッグ中に、コードの一部の行をスキップする必要があることがよくあります。 いくつかの一般的な代替手段があるため、これにgoto手法を使用する必要はありません。

4.1. コメントアウト

はるかに、いくつかのコードを無視するための最も簡単な解決策は、それをコメントアウトすることです。

Bashはハッシュ記号「#」を使用して、ハッシュ記号から行末までのすべての文字を無視します。

#!/bin/bash

echo "This shows up in terminal"
#echo "but this not"

残念ながら、bashで利用できるブロックコメントはありません。

4.2. ifステートメントでのfalse関数の使用

ifステートメントでfalseを使用できます。

#!/bin/bash

if false
then
    echo "Disabled code here"
fi

echo "Not disabled code here"

コードを再度有効にするために、falsetrueに置き換えることができます。

4.3. Bash変数の設定

または、コマンドライン引数から値を取得する環境変数またはBash変数を使用して、コード条件を作成することもできます。

DEBUG環境変数があると想像してみましょう。

#!/bin/bash

if [ -n "$DEBUG" ]
then
    echo "Disabled code here"
fi

echo "Not disabled code here"	

-n スイッチは、変数が空の文字列でないかどうかをテストします。 また、DEBUGは任意の値に設定できます。

このバージョンの利点は、動作を変更するためにスクリプトを変更する必要がないことです。

代わりに、変数を設定してエクスポートするだけです。

export DEBUG=true
./debug_test

#result:
Disabled code here
Not disabled code here

または、スクリプトを開始する前に設定を解除します。

unset DEBUG
./debug_test

#result:
Not disabled code here

5. マルチレベルループで飛び回る

ほとんどのプログラミング言語はbreakおよびcontinueステートメントを提供しますが、多くの場合、それらの効果は直接囲んでいるループにのみ到達します。

対照的に、Bashはbreaknとcontinuenを提供します。

5.1. マルチレベルループの解除

break nスキップnレベルのループ。条件が満たされたときに、ネストされたループからジャンプして、すべての反復を効果的に停止してみましょう。

#!/bin/bash

for x in {1..10}
do
    echo "x = $x"
    for y in {1..5}
    do
        echo "    y = $y"
	if [ $x -ge 2 ] && [ $y -eq 3 ]
	then
	    echo "Breaking"
	    break 2
	fi	
    done
done
echo "Now all loops are done"

結果を調べてみましょう。

x = 1
    y = 1
    y = 2
    y = 3
    y = 4
    y = 5
x = 2
    y = 1
    y = 2
    y = 3
Breaking
Now all loops are done

一度条件が [$ x -ge 2] && [$ y -eq 3] 満たされている、 両方のループが終了し、制御はそれらの後の最初のステートメントに渡されます

5.2. マルチレベルループの継続

continue n コマンドはループ内の操作を省略し、はネストされたループのnレベルをジャンプできるようにします

n レベルを介して、現在のループの残りの部分だけでなく、すべての囲んでいるループの本体も省略されます。

#!/bin/bash

for x in {1..5}
do
    for y in {1..3}
    do
        if [ $x -gt 2 ] && [ $y -eq 2 ]
	then
	    echo "Continue"
	    continue 2
	fi
	echo "    y = $y"	
    done
    echo "x = $x"	
done
echo "Now all loops are done"

それでは、結果を調べてみましょう。

    y = 1
    y = 2
    y = 3
x = 1
    y = 1
    y = 2
    y = 3
x = 2
    y = 1
Continue
    y = 1
Continue
    y = 1
Continue
Now all loops are done

ループ変数が条件を満たしたら [$ x -gt 2] && [$ y -eq 2] 継続ステートメントが実行された後、操作はありません

さらに、変数xを持つ外部ループにも関係します。

6. エラー処理

エラー処理は、フロー制御によって対処できるもう1つの懸念事項です。

6.1. モデル制御フロー

この擬似コードは、エラーに固有のアクションを実行する方法、またはプログラムを再開する方法を示しています。

    if ( !functionA() ) goto errorA
    if ( !functionB() ) goto errorB
    #...

    exit(0) # everything is OK

errorA:
    concludeA() #some specific A fallback
    exit(codeA)
errorB:
    concludeB() #some specific B fallback
    exit(codeB)

6.2. ケーススタディ

ディレクトリを特定のディレクトリに変更し、続いてこのディレクトリ内のファイルに新しい行を書き込むスクリプトを作成しましょう。

これらの操作は両方とも、独自のエラーを発行する可能性があります。たとえば、ターゲットフォルダーが存在しない場合や、ユーザーにファイルへの書き込み権限がない場合などです。

6.3. some_action || handle_error構成

Bashでは、ステートメント Separator || 左側のコマンドが失敗した場合にのみ、右側のコマンドを実行します

したがって、 some_action の部分がエラーが発生しやすいコードであり、handle_errorが適切なフォールバック関数であると仮定します。

#!/bin/bash

target_folder="test"
target_file="foo"

cd "$target_folder" || { echo "Can not cd to $target_folder" && exit 1; }

echo -n > "$target_file" || { echo "Can not write to file $target_file in $target_folder" && exit 1; }

echo "Successfully wrote new line to file $target_file in $target_folder"

6.4. スクリプトのテスト

最初に、「test」フォルダが存在しないと仮定しましょう。

./error_example
./error_example: line 6: cd: test: No such file or directory
Can not cd to test

それでは、フォルダを作成しましょう。

mkdir test #create a folder
./error_example Successfully wrote new line to file foo in test

そして最後に、ユーザーから書き込み権限を奪います。

chmod u-w test/foo #take away the write permission from the user
./error_example
./error_example: line 8: foo: Permission denied
Can not write to file foo in test

通常のBashエラーメッセージだけでなく、期待どおりに通知も受け取ることを確認しましょう。

6.5. スクリプトのハイライト

スクリプトerror_exampleの重要な機能に注目しましょう。

  • エラー処理コマンドをブロックに入れる必要があります。
  • エラー処理側で中括弧{}を使用する必要がありますエラーでスクリプトを終了します。
  • 右側では、別のものを使用しますセパレーター && 両方のコマンドの実行を強制するには、 エコー 、 と出口

7. 結論

この記事では、Bashの機能を使用して制御の流れを変更する方法を学びました。

まず、フォワードを模倣しました後藤によって猫< 工事。 次に、スクリプトの一部をバイパスする他の方法を検討しました。

マルチレベルのネストされたループを処理するために、Bashのbreakcontinueに移動しました。 最後に、bashコマンドをチェーンする方法を学びました。 || && 演算子。