Gotoを使用しないBashのフロー制御
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でネイティブに行うことはできませんが、オペレーター <
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
ご覧のとおり、catとLABEL_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_CONDITIONがtrue の場合に実行される部分は、以前と同様に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
ここでは、すべてのスクリプトが実行されたことがわかります。 さらに、両方の変数が適切に定義され、表示されます。
最後に、ローカル変数fooとbarを正しく使用すると、スクリプトが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"
コードを再度有効にするために、falseをtrueに置き換えることができます。
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のbreakとcontinueに移動しました。 最後に、bashコマンドをチェーンする方法を学びました。 || と && 演算子。