【问题标题】:rsync suppress SIGINT for trap outsidersync 抑制外部陷阱的 SIGINT
【发布时间】:2020-02-27 16:10:24
【问题描述】:

我正在尝试构建一个通用的retry shell 函数,以便在上次失败时重新运行指定的 shell 命令几次,这是我的代码:

retry() {
    declare -i number=$1
    declare -i interrupt=0
    trap "echo Exited!; interrupt=1;" SIGINT SIGTERM SIGQUIT SIGKILL

    shift

    for i in `seq $number`; do
      echo "\n-- Retry ${i}th time(s) --\n"

      $@

      if [[ $? -eq 0 || $interrupt -ne 0 ]]; then
        break;
      fi
    done
}

它适用于wgetcurl 和其他各种常用命令。但是,如果我运行

retry 10 rsync local remote

,在传输过程中发送一个ctrl+c中断它,它会报告

rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(700) [sender=3.1.3]

似乎rsync 抑制了内部的 SIGINT 和其他一些相关信号,然后将代码 20 返回给外部调用者。这个返回码没有使循环中断,然后我发送了几个 ctrl+c 来中断下一个rsync 命令。它只为最后一个 ctrl+c 打印 Exited! 并且 trap 捕获它。

问题

  1. 为什么它首先返回代码 20 没有使循环中断?

  2. 如何让trap捕捉到SIGINT信号而rsync,如果没有,我该怎么办?

【问题讨论】:

  • 当 rsync 以 20 失败时,您已经知道它无论如何“失败”了,它不会影响您的重试逻辑。那为什么重要呢?
  • 我想在按下ctrl+c时立即终止命令并退出循环,不管子命令是什么。 @P.P
  • 有道理。我已经发布了一个解决方案,应该可以按照您的意愿工作。

标签: bash shell signals rsync bash-trap


【解决方案1】:

为什么它首先返回代码 20 没有使循环中断?

您是正确的,rsync 确实捕获了某些信号并以RERR_SIGNAL (20) 退出。

如何让trap捕捉到SIGINT信号却rsync,如果没有,怎么办?

由于rsync 有自己的处理程序,你不能做任何事情(可以使用一些技巧来覆盖rsync 中的信号处理程序,例如LD_PRELOAD。但这可能是不必要的复杂)。由于您的陷阱位于当前外壳中,因此您不知道“命令”是否已发出信号或以非零退出。

我假设您希望您的 retry 是通用的,并且您不希望对 rsync 进行特殊处理(例如,不同的命令可能会以 75 退出信号并且您不想处理特殊情况)。

问题是您的陷阱处理程序未激活,因为当前进程正在运行进程 (rsync) 接收到信号。您可以改为在后台运行命令并等待它完成。这将允许您从retry 捕获信号。收到信号后,它会简单地杀死子进程。

#!/bin/bash

retry()
{
    declare -i number=$1
    declare -i i
    declare -i pid
    declare -i interrupted=0

    trap "echo Exiting...; interrupted=1" SIGINT SIGTERM SIGQUIT
    shift

    # Turn off "monitor mode" so the shell doesn't report terminating background jobs.
    set +m

    for ((i = 0; i < number; ++i)); do
        echo "\n-- Retry ${i}th time(s) --\n"
        $@ &
        pid=$!

        # If command succeeded, break
        wait $pid && break

        # If we receive one of the signals, break
        [[ $interrupted == 1 ]] && kill $pid && break
    done

    # Switch back to default behaviour
    set -m
    trap - SIGINT SIGTERM SIGQUIT
}

请注意,SIGKILL 无法被捕获。因此,为它设置陷阱是没有意义的。所以我把它删除了。

【讨论】:

  • 谢谢!在这里学到了很多东西,我在最后的编辑中合并了以上两个功能以供我使用。 :)
  • 没问题。我个人更喜欢使用单独的函数进行信号处理。但没有技术原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
相关资源
最近更新 更多