典型的な命令型プログラミングでは、命令のシーケンスを記述し、明示的な制御フローで次々に実行されます。例えば:
if [ -f file1 ]; then # If file1 exists ...
cp file1 file2 # ... create file2 as a copy of a file1
fi
等
この例からわかるように、命令型プログラミングでは、実行フローを非常に簡単にたどり、コードの特定の行から常に上に向かって実行コンテキストを決定します。フロー内の場所(または関数を記述している場合は、呼び出しサイトの場所)。
コールバックがフローを変更する方法
コールバックを使用する場合、一連の命令を「地理的に」使用する代わりに、いつ呼び出す必要があるかを記述します。他のプログラミング環境での典型的な例は、「このリソースをダウンロードし、ダウンロードが完了したら、このコールバックを呼び出す」などのケースです。Bashにはこの種の汎用のコールバック構造はありませんが、エラー処理やその他のいくつかの状況のためにコールバックがあります。例(コマンド置換とBash 終了モードを最初に理解してから、その例を理解する必要があります):
#!/bin/bash
scripttmp=$(mktemp -d) # Create a temporary directory (these will usually be created under /tmp or /var/tmp/)
cleanup() { # Declare a cleanup function
rm -rf "${scripttmp}" # ... which deletes the temporary directory we just created
}
trap cleanup EXIT # Ask Bash to call cleanup on exit
これを自分で試してみたい場合は、上記をファイルに保存し、たとえばcleanUpOnExit.sh
、実行可能にして実行してください:
chmod 755 cleanUpOnExit.sh
./cleanUpOnExit.sh
ここでの私のコードは、cleanup
関数を明示的に呼び出すことはありません。それは、Bashにを使用していつ呼び出すかを指示しますtrap cleanup EXIT
。つまり、「親愛なるBash、cleanup
終了したらコマンドを実行してください」(cleanup
たまたま先ほど定義した関数ですが、Bashが理解できるものなら何でもかまいません)。Bashは、すべての致命的ではないシグナル、終了、コマンドの失敗、および一般的なデバッグに対してこれをサポートします(すべてのコマンドの前に実行されるコールバックを指定できます)。ここでのコールバックはcleanup
関数であり、シェルが終了する直前にBashによって「コールバック」されます。
Bashの機能を使用して、シェルパラメーターをコマンドとして評価し、コールバック指向のフレームワークを構築できます。それはこの答えの範囲をやや超えており、おそらく関数を渡すことは常にコールバックを伴うことを示唆することでより混乱を引き起こすでしょう。基本機能のいくつかの例については、Bash:関数をパラメーターとして渡すをご覧ください。ここでの考え方は、イベント処理コールバックの場合と同様に、関数はデータをパラメーターとして使用できますが、他の関数も使用できるということです。これにより、呼び出し側はデータだけでなく動作も提供できます。このアプローチの簡単な例は次のようになります
#!/bin/bash
doonall() {
command="$1"
shift
for arg; do
"${command}" "${arg}"
done
}
backup() {
mkdir -p ~/backup
cp "$1" ~/backup
}
doonall backup "$@"
(cp
複数のファイルを扱うことができるので、これは少し役に立たないことを知っています、それは説明のためだけです。)
ここでdoonall
、パラメーターとして指定された別のコマンドを受け取り、そのパラメーターの残りに適用する関数を作成します。次に、それを使用しbackup
て、スクリプトに指定されたすべてのパラメーターで関数を呼び出します。その結果、すべての引数を1つずつバックアップディレクトリにコピーするスクリプトが作成されます。
この種のアプローチにより、関数を単一の責任で記述することができます。doonall
の責任は、すべての引数に対して何かを1つずつ実行することです。backup
の責任は、バックアップディレクトリにその(唯一の)引数のコピーを作成することです。両方doonall
及びbackup
他のコンテキストで使用することができ、より多くのコード再利用、より良いテスト等を可能にします
この場合、コールバックはbackup
関数でありdoonall
、他の各引数に対して「コールバック」するように指示しdoonall
ます。データ(残りの引数)と同様に動作(最初の引数)を提供します。
(2番目の例で示したようなユースケースでは、「コールバック」という用語は使用しませんが、それはおそらく使用する言語に起因する習慣です。これは、関数またはラムダを渡すことと考えられます。 、コールバックをイベント指向システムに登録するのではなく。)