「グローバル」スコープでトラップコマンドを実行したいのですが、信号は関数内から送信されます。もちろん、事前に変数をグローバルに宣言することも、-g
宣言オプションを使用することもできます。しかし、以下に示すように、あまり実用的ではないトラップで調達したい場合:
#!/bin/bash
# ./variables
declare say=hello
declare -ri times 3
および実際のスクリプト:
#!/bin/bash
# ./trapsource
setTraps () {
trap 'echo trap called in scope ${FUNCNAME[@]}; source ./variables' SIGUSR1
}
sourceVariables () {
kill -SIGUSR1 $$
}
setTraps
sourceVariables
echo I want you to $say \"hello\" $times times.
printf "$say\n%.0s" $(seq 1 $times)
しかし、declare -g NAME
なしで、ありがとう。
編集:kill -s USR1 $$
部分を現在のプロセスから完全に切り離して、信号が「外部」から来るようにすることは可能ですか?
Nohup
とdisown
を試しましたが、今のところ成功していません。
編集2:真のソリューションを実現できる方向を示すのに十分なbashカスタムビルトインの経験はありますか?
trap 'echo trap called in scope ${FUNCNAME[@]}; declare say=hello; declare -ri times=3' SIGUSR1
トラップ内でdeclare -i
を使用する代わりに、事前にそれを実行し、トラップに新しい値を割り当てるだけです。
declare -i times=999
trap 'times=3' USR1
readonly
自体は変数をローカルにしないので、トラップ内でreadonly times
を使用することもできると思います。
例えば。これにより、1、3、3が出力され、読み取り専用変数の変更中にエラーが発生します。
#!/bin/bash
trap 'readonly num=3' USR1
sub() {
kill -USR1 $$
echo "$num"
}
declare -i num=999
num=1
echo "$num"
sub
echo "$num"
num=1234
繰り返しますが、変更するグローバル変数の場合は、declare -g
を使用してみませんか?
kill -s USR1 $$
部分を現在のプロセスから完全に切り離して、信号が「外部」から来るようにすることは可能ですか?
(組み込みの)kill
によってスクリプト自体から送信された信号と、別のプロセスによって送信された信号に違いはないと思います。ご覧のとおり、Bashは実行中の関数のコンテキストでトラップコードを実行しているようです。
#!/bin/bash
setTraps () {
trap 'echo trap called in scope ${FUNCNAME[@]}; say=hello' USR1
}
sendSignal () {
kill -s USR1 "$$"
}
setTraps
sendSignal
printf 'I want you to %s "hello"\n' "$say"
declare
をまったく使用しない場合、トラップはsay
変数をグローバルスコープの文字列に設定します。トラップはまだ呼び出されたsendSignal
スコープの範囲内ですが、それを変更する方法はないと思います。
Kill -s USR1 $$の部分を現在のプロセスから完全に切り離して、信号が「外部」から来るようにすることは可能ですか?
問題は、シグナルが「内部」から来るということではなく、メインスクリプトが関数スコープ内で実行されている間にそのシグナルを受信することです。 「外部」から信号を送信するだけでは不十分です。そうでない場合は、(kill -SIGUSR1 $$)
のような単純なサブシェルで十分です。また、メインスクリプトがsourceVariables
関数から戻り、trap
を実行する他のスコープを入力する機会を与える方法で送信する必要があります。変数を明示的にマークせずに「グローバル」にしたい場合は、おそらくメインスコープです。
サンプルコードの場合、バックグラウンドでsourceVariables
関数を実行するだけです。そうすれば、トラップは確実にメインスコープで実行されます。
たとえば、次のコードは(私が思うに)あなたが意図したとおりに動作します:
#!/bin/bash
set -x # <-- set debugging to see what happens
setTraps () {
trap 'echo trap called in scope ${FUNCNAME[@]}; source ./variables' SIGUSR1
}
sourceVariables () {
kill -SIGUSR1 $$
}
setTraps
sourceVariables &
while ! [ $say ] ; do :; done # <-- careful: cpu-intensive loop, only for demonstration
echo I want you to $say \"hello\" $times times.
printf "$say\n%.0s" $(seq 1 $times)
ただし、次の要件は、実際のsourceVariables
関数(これまでに示したものではない)をすべてバックグラウンドで実行してはならないということだと思います。
その場合は、kill
とスクリプトの間に同期メカニズムを設定して、すべてが適切なタイミングで行われるようにする必要もあります。さまざまな方法がありますが、実際のアプリケーションによって最適な方法が異なります。
coproc
ビルトインを使用した単純なもので、Bash v4 +が必要です。
#!/bin/bash
set -x # <-- set debugging to see what happens
setTraps () {
trap 'echo trap called in scope ${FUNCNAME[@]}; source ./variables' USR1
}
sourceVariables () {
# Do interesting stuff
coproc { read ; kill -USR1 $$ ; } # run a coprocess in background
# the coprocess starts by waiting on `read`, which serves as a "go-ahead notification"
# from the script when this latter is ready to receive the signal
# Do yet more interesting stuff
}
setTraps
sourceVariables
# do even more stuff not yet ready for USR1
echo >&${COPROC[1]} # notify the coprocess that we're now ready to receive the signal
while ! [ $say ] ; do :; done # <-- careful: cpu-intensive loop, only for demonstration
echo I want you to $say \"hello\" $times times.
printf "$say\n%.0s" $(seq 1 $times)