bashに「goto」ステートメントはありますか?私はそれが悪い習慣と考えられていることを知っていますが、私は特に「後藤」が必要です。
goto大きなスクリプトをデバッグするために、関係のないさまざまなタスクが完了するのを1時間待たずにステートメントで多くのコードをスキップするため、この質問が見つかりました。私は確かにgoto本番コードでa を使用しませんが、私のコードをデバッグするために、それは私の人生を無限に容易にし、それを削除するようになったときにそれを見つけるのが簡単になります。
bashに「goto」ステートメントはありますか?私はそれが悪い習慣と考えられていることを知っていますが、私は特に「後藤」が必要です。
goto大きなスクリプトをデバッグするために、関係のないさまざまなタスクが完了するのを1時間待たずにステートメントで多くのコードをスキップするため、この質問が見つかりました。私は確かにgoto本番コードでa を使用しませんが、私のコードをデバッグするために、それは私の人生を無限に容易にし、それを削除するようになったときにそれを見つけるのが簡単になります。
回答:
いいえ、ありません; ご覧に§3.2.4「化合物のコマンドを」手動バッシュリファレンス制御構造については行うが存在します。特に、breakおよびの言及に注意してくださいcontinue。これらは、ほど柔軟ではありませんgotoが、Bashでは一部の言語よりも柔軟であり、目的を達成するのに役立つ場合があります。(あなたが望むものは何でも……)
breakか、continueバッシュは、あなたがジャンプするどのように多くのループのレベルを指定することができます一方、最も内側のループから。(さらにあなたを許す言語のbreakかcontinue、例えば-任意のループから、ほとんどはそれが静的に表現する必要がbreak foo;ラベルされたループから抜け出すだろうfoo- Bashの中で、それは動的に表現しています一方で-例えば、break "$foo"から抜け出すだろう$fooループ。 )
break LABEL_NAME;では、Bashの嫌なものではなく、を許可していbreak INTEGER;ます。
デバッグのために大きなスクリプトの一部をスキップするためにそれを使用している場合(Karl Nicollのコメントを参照)、falseが適切なオプションである可能性があります(「false」が常に使用可能かどうかはわかりませんが、/ bin / falseにあります)。 :
# ... Code I want to run here ...
if false; then
# ... Code I want to skip here ...
fi
# ... I want to resume here ...
デバッグコードを削除するときがきます。「偽の場合」の構成は非常に単純で覚えやすいものですが、一致するfiをどのようにして見つけますか?エディタでインデントをブロックできる場合は、スキップしたブロックをインデントすることができます(完了したら元に戻す必要があります)。または、fi行のコメントですが、覚えておく必要があります。プログラマーに依存していると思います。
false、いつでもご利用いただけます。ただし、実行したくないコードブロックがある場合は、コメントアウトしてください。またはそれを削除します(後で回復する必要がある場合は、ソース管理システムを調べます)。
確かに、デバッグやデモンストレーションのニーズに役立つ場合があります。
ボブコープランドソリューションhttp://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:
#!/bin/bash
# include this boilerplate
function jumpto
{
label=$1
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $start
start:
# your script goes here...
x=100
jumpto foo
mid:
x=101
echo "This is not printed!"
foo:
x=${x:-10}
echo x is $x
結果は:
$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101
: start:が構文エラーではないようにラベルを開始するようにすることです。
set -x上で何が起こっているか理解するのに役立ちます
casebashでgotoをシミュレートするために使用できます。
#!/bin/bash
case bar in
foo)
echo foo
;&
bar)
echo bar
;&
*)
echo star
;;
esac
生成する:
bar
star
bash v4.0+です。ただし、これは汎用ではgotoなく、caseステートメントのフォールスルーオプションです。
bashスクリプトをテスト/デバッグしていて、コードの1つまたは複数のセクションを過ぎて前方にスキップするだけの場合は、これを実行する非常に簡単な方法を次に示します(ほとんどのメソッドとは異なり)。上記に記載)。
#!/bin/bash
echo "Run this"
cat >/dev/null <<GOTO_1
echo "Don't run this"
GOTO_1
echo "Also run this"
cat >/dev/null <<GOTO_2
echo "Don't run this either"
GOTO_2
echo "Yet more code I want to run"
スクリプトを通常の状態に戻すには、で行を削除しGOTOます。
gotoコマンドをエイリアスとして追加することで、このソリューションを偽装することもできます。
#!/bin/bash
shopt -s expand_aliases
alias goto="cat >/dev/null <<"
goto GOTO_1
echo "Don't run this"
GOTO_1
echo "Run this"
goto GOTO_2
echo "Don't run this either"
GOTO_2
echo "All done"
エイリアスは通常bashスクリプトでは機能しないため、それをshopt修正するコマンドが必要です。
を有効/無効にしたい場合はgoto、もう少し必要です。
#!/bin/bash
shopt -s expand_aliases
if [ -n "$DEBUG" ] ; then
alias goto="cat >/dev/null <<"
else
alias goto=":"
fi
goto '#GOTO_1'
echo "Don't run this"
#GOTO1
echo "Run this"
goto '#GOTO_2'
echo "Don't run this either"
#GOTO_2
echo "All done"
その後export DEBUG=TRUE、スクリプトを実行する前に行うことができます。
ラベルはコメントなので、goto「」を無効にしても構文エラーは発生しません(goto「:」にが、これは、gotoステートメントで。
あらゆる種類のgotoソリューションを使用するときは常に、ジャンプするコードが後で依存する変数を設定しないように注意する必要があります。これらの定義をスクリプトの先頭またはそのすぐ上に移動する必要がある場合があります。あなたのgoto声明の。
if(false)(私にとっては)より理にかなっているようです。
alias goto=":<<"省くこともできcatます。
他のユーザーはすでにgotobashに直接同等のものがないことを明確にしています(そして関数、ループ、ブレークなどの最も近い代替手段を提供しています)が、loop plusを使用しbreakて特定のタイプのgotoステートメントをシミュレートする方法を説明したいと思います。
これが最も役立つと思う状況は、特定の条件が満たされない場合にコードのセクションの最初に戻る必要がある場合です。以下の例では、pingがテストIPへのパケットのドロップを停止するまで、whileループが永久に実行されます。
#!/bin/bash
TestIP="8.8.8.8"
# Loop forever (until break is issued)
while true; do
# Do a simple test for Internet connectivity
PacketLoss=$(ping "$TestIP" -c 2 | grep -Eo "[0-9]+% packet loss" | grep -Eo "^[0-9]")
# Exit the loop if ping is no longer dropping packets
if [ "$PacketLoss" == 0 ]; then
echo "Connection restored"
break
else
echo "No connectivity"
fi
done
このソリューションには次の問題がありました。
:label:任意の場所をラベルとして扱いますこれは修正済み(shell-checkクリーン)バージョンです。
#!/bin/bash
# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
function goto
{
local label=$1
cmd=$(sed -En "/^[[:space:]]*#[[:space:]]*$label:[[:space:]]*#/{:a;n;p;ba};" "$0")
eval "$cmd"
exit
}
start=${1:-start}
goto "$start" # GOTO start: by default
#start:# Comments can occur after labels
echo start
goto end
# skip: # Whitespace is allowed
echo this is usually skipped
# end: #
echo end
gotoバッシュにはありません。
trap後方にのみジャンプするダーティな回避策があります:)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
出力:
$ ./test.sh
foo
I am
here now.
これはそのように使用するべきではなく、教育目的でのみ使用してください。これが機能する理由は次のとおりです。
trap例外処理を使用して、コードフローの変更を実現しています。この場合、trapはスクリプトが終了する原因となるものをすべてキャッチしています。コマンドgotoが存在しないため、通常はスクリプトを終了するエラーがスローされます。このエラーはでキャッチされtrap、2>/dev/nullは通常表示されるエラーメッセージを非表示にします。
このgotoの実装は、存在しないコマンド(またはその方法による他のエラー)が同じトラップコマンドを実行するため、明らかに信頼できません。特に、移動先のラベルを選択することはできません。
基本的に、実際のシナリオではgotoステートメントは必要ありません。異なる場所へのランダムな呼び出しはコードを理解することを難しくするだけなので、それらは冗長です。
あなたのコードが何度も呼び出された場合は、ループを使用することを考慮して使用することにそのワークフローを変更するcontinueとbreak。
コードがそれ自体を繰り返す場合は、関数を記述して、必要な回数だけ呼び出すことを検討してください。
コードが変数値に基づいて特定のセクションにジャンプする必要がある場合は、使用を検討してください caseステートメントのをください。
長いコードを細かく分割できる場合は、コードを個別のファイルに移動して、親スクリプトから呼び出すことを検討してください。
trapを使用exception handlingしていると考えてください。この場合、トラップはスクリプトを発生させるあらゆるものをキャッチしていますEXIT。これは、存在しないコマンドを呼び出すことによってトリガーされgotoます。ところで、EXITを引き起こすのは引数なのでgoto trap、引数は何でもかまいません。スクリプトは終了することを示すエラーメッセージを非表示にします。goto ignoredgoto2>/dev/null
これは、Hubbbitusが出したJudy Schmidtスクリプトの小さな修正です。
スクリプトにエスケープされていないラベルを付けると、マシンで問題が発生し、クラッシュしました。これは、ラベルをエスケープする#を追加することで簡単に解決できました。Alexej Maguraとaccess_grantedの提案に感謝します。
#!/bin/bash
# include this boilerplate
function goto {
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
goto $start
#start#
echo "start"
goto bing
#boom#
echo boom
goto eof
#bang#
echo bang
goto boom
#bing#
echo bing
goto bang
#eof#
echo "the end mother-hugger..."
私は関数を使用してこれを行う方法を見つけました。
:言うには、たとえば、あなたは3つの選択肢を持っているA、BとC。コマンドAをB実行しCますが、詳細情報が表示され、元のプロンプトに戻ります。これは関数を使用して行うことができます。
行containsg function demoFunctionは関数を設定するだけなdemoFunctionので、関数が実際に実行されるように、そのスクリプトの後に呼び出す必要があることに注意してください。
GOTOシェルスクリプトの別の場所に「」が必要な場合は、他の複数の関数を記述して呼び出すことで、これを簡単に調整できます。
function demoFunction {
read -n1 -p "Pick a letter to run a command [A, B, or C for more info] " runCommand
case $runCommand in
a|A) printf "\n\tpwd being executed...\n" && pwd;;
b|B) printf "\n\tls being executed...\n" && ls;;
c|C) printf "\n\toption A runs pwd, option B runs ls\n" && demoFunction;;
esac
}
demoFunction
gotobashにはありません(少なくともそれはcommand not found私に言っています)。どうして?それを行うにはもっと良い方法があるでしょう。