関数から「グローバル」bash範囲トラップ信号をエクスポートする方法は?

関数から「グローバル」bash範囲トラップ信号をエクスポートする方法は?

「グローバル」範囲でトラップコマンドを実行したいのですが、信号は関数の内部から来ます。もちろん、変数は事前にグローバルに宣言することも、-g宣言オプションを使用して宣言することもできます。しかし、次のようにトラップのソースを取得したい場合は、あまり実用的ではありません。

#!/bin/bash
# ./variables
declare say=hello
declare -i times=4

実際のスクリプトは次のとおりです。

#!/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まで成功しませんでした。

PS:@LL2は、ここでバックグラウンドプロセスまたはcoprocを使用するソリューションを提供します。https://unix.stackexchange.com/a/518720/154557 しかし、時間同期の問題が発生し、コードが非常に複雑になるか心配です。

PPS:実際、@LL2ソリューションはIPCでもうまく機能するため、この問題は解決されているようです。欠点は、デフォルトのスコープで追加の使用が必要な場合はサブシェルのシンボルが失われるため、関数パラメータをソースファイルで処理する必要があることです。見て編集2この問題に対する別の解決策。

#!/bin/bash
# ./trapsource
set -x  # <-- set debugging to see what happens

setTraps () {
  trap 'declare IPC=./ipc.fifo; \
    [ -p $IPC ] \
    && exec {IPCFD}<>$IPC \
    && { read -a ARGS <&$IPCFD; eval "exec $IPCFD>&-"; } \
    && source "${ARGS[@]}" \
    && IPC_RCV=true' SIGUSR1
}

sourceVariables () {
  declare IPC=./ipc.fifo
  declare IPC_RCV=false
  [ ! -p $IPC ] \
  && mkfifo $IPC
  [ $# -gt 0 ] \
  && exec {IPCFD}<>$IPC \
  && echo "${@:2}" >&$IPCFD \
  && eval "exec $IPCFD>&-" \
  && kill -s USR1 $1
}

test () {
  sourceVariables "$@" &
}

setTraps
test $$ ./variables a b c
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)
printf "'%s' " "${args[@]}"

編集2(提案3進行中):

実際の解決策を達成するための方向性を教えるのに十分なbashカスタム組み込み機能の経験がある人はいますか?

PS:Bashのロード可能な組み込み機能は非常に強力で、デフォルトの範囲でdbusメソッド呼び出しを受け取るために最もよく使用されます。

ソリューションII(bgまたはcoprocなし):

信号は常に現在の実行ラインの範囲内で発生するようです。おそらく、カスタムbash組み込み機能(非常に強力なロード可能な組み込み機能)を使用するより複雑な別の解決策があります。 IPC用bash-dbus-builtinsも考えられます。とにかくエイリアスを使用するソリューションは、ソーススクリプトで -g グローバルフラグ ./variable を設定しなくても、シグナルの一部のスクリプトを取得するためにバックグラウンドで眠っている自分/他の PID に指示する場合に便利です。したがって、./変数必要に応じて、スクリプトを関数の範囲内で具体的に使用することもできます。エイリアスはサブシェルにエクスポートできないため、BASH_ENV変数によって参照される環境スクリプトにエイリアスを含める必要があります。ここでは、ユースケースの目標をより明確にするために、IPCメカニズムを使用して元の例を拡張しました。

これで、変数リポジトリのパラメータも許可されます。

#!/bin/bash
# ./variables
declare say=hello
declare -i times=4
declare -a args=("$@")

まず、環境スクリプトを準備します。

#!/bin/bash
# ./env
shopt -s expand_aliases
alias source_ipc='source ./ipc'
alias source_ipc_rcv='source ./ipc_rcv'

そして、すべてのbashスクリプトでエイリアスが利用可能であることを確認してください。

$> export BASH_ENV=./env

また、fifo を使用する実際の IPC 送受信メカニズムが必要です。

#!/bin/bash
# ./ipc

declare IPC=./ipc.fifo
declare IPC_RCV=false
[ ! -p $IPC ] \
&& mkfifo $IPC
[ $# -gt 0 ] \
&& exec {IPCFD}<>$IPC \
&& echo "${@:2}" >&$IPCFD \
&& eval "exec $IPCFD>&-" \
&& kill -s USR1 $1

受信するにはIPCを追加してください。

#!/bin/bash
# ./ipc_rcv

declare IPC=./ipc.fifo
[ -p $IPC ] \
&& exec {IPCFD}<>$IPC \
&& { read -a ARGS <&$IPCFD; eval "exec $IPCFD>&-"; } \
&& source "${ARGS[@]}" \
&& IPC_RCV=true

これで、複数のプロセスにわたってソース信号を透過的にトリガーできます。

#!/bin/bash
# ./trapsource u+x

trap 'source_ipc_rcv' SIGUSR1

log="$0.log"
err="$0.err.log"
exec 1>$log
exec 2>$err

# test inside script
source_ipc $$ ./variables a b c

while true; do
  echo I want you to \"$say\" $times times.
  if $IPC_RCV; then
    printf "$say\n%.0s" $(seq 1 $times)
    printf "'%s' " "${args[@]}"
  fi
  sleep 1
done

プロセス名前空間「外部」では、次のように信号がトリガーされることがあります。 BASH_ENV参照ですでにAliasを提供しているとします。

$> ./trapsource & TSPID=$!; sleep 1; source_ipc $TSPID ./variables arg1 arg2; sleep 2; kill $TSPID

ベストアンサー1

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 timesので、トラップで使用することもできると思います。readonly

たとえば、これを行うと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は実行中の関数のコンテキストでトラップコードを実行しているようです。

おすすめ記事