함수에서 "전역" 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까지 성공하지 못했습니다.

추신: @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 사용자 정의 내장 기능에 대한 충분한 경험이 있는 사람이 있습니까?

추신: 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는 실행 중인 함수의 컨텍스트에서 트랩 코드를 실행하는 것으로 보입니다.

답변2

신호가 "외부"에서 나오도록 현재 프로세스에서 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)

답변3

#!/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이를 변경할 수 있는 방법이 없습니다.

관련 정보