AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 500894
Accepted
domson
domson
Asked: 2019-02-16 07:06:54 +0800 CST2019-02-16 07:06:54 +0800 CST 2019-02-16 07:06:54 +0800 CST

如何从函数发出“全局”bash范围内的陷阱?

  • 772

我想在“全局”范围内执行陷阱命令,但信号将来自函数内部。当然,可以预先全局声明变量或使用-gdeclare 选项。但是如果我想在陷阱上获取资源,这不是很实用,如下所示:

#!/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,所以我认为问题已解决。一个缺点是,如果需要在主范围内进一步使用,必须在源文件中处理函数参数,因为子shell中的符号当然会丢失。查看EDIT 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 Loadable Builtins 非常强大,很可能用于接收主范围内的 dbus 方法调用。

解决方案二(没有 bg 也没有 coproc):

看起来信号总是在当前执行行的范围内触发。很可能还有另一种更复杂的解决方案,使用自定义 bash 内置函数(非常强大的可加载内置函数)。我什至可以想到 IPC 的 bash-dbus-builtins。无论如何,如果一个人想告诉自己/另一个在后台休眠的 PID 在信号源上获取一些脚本,而不必在源脚本 ./variables 中设置 -g 全局标志,则使用别名的这种解决方案可能很有用,所以 ./变量脚本也可以在必要时在函数范围内独占使用。由于无法将别名导出到子shell,因此必须将别名包含在 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 参考的来源。

$> ./trapsource & TSPID=$!; sleep 1; source_ipc $TSPID ./variables arg1 arg2; sleep 2; kill $TSPID
bash signals
  • 3 3 个回答
  • 1689 Views

3 个回答

  • Voted
  1. ilkkachu
    2019-02-17T06:11:05+08:002019-02-17T06:11:05+08:00
    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
  2. Kusalananda
    2019-02-17T03:21:57+08:002019-02-17T03:21:57+08:00
    #!/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,但我认为没有办法改变它。

    • 1
  3. Best Answer
    LL3
    2019-05-14T09:07:50+08:002019-05-14T09:07:50+08:00

    是否可以将 kill -s USR1 $$ 部分完全从当前进程中分离出来,以便信号来自“外部”?

    问题不在于您的信号来自“内部”,而是您的主脚本在它仍在函数范围内运行时接收到该信号。从“外部”发送信号是不够的,否则一个简单的子shell(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)
    
    • 1

相关问题

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve