上下文:备份软件执行钩子脚本

上下文:备份软件执行钩子脚本

上下文:备份软件执行钩子脚本

我有一个用例,其中备份软件能够在执行某些备份之前、之后等执行 shell 脚本作为钩子。这样做是为了可选地设置一个可以安全进行备份的环境,例如通过创建文件系统级快照、安装这些快照并从安装中读取备份。因此,该备份软件默认会等到钩子脚本完成。

我通过在该钩子脚本中执行额外的后台进程来实现一个略有不同的用例,以便使用 SSH 从某些虚拟机中读取数据,并将读取的内容输出到命名管道中。写入命名管道会阻塞,直到读取器连接上,反之亦然,因此启动的后台进程确实需要与作为管道读取器的备份软件同时执行。要做到这一点,主钩子脚本需要完成,以便等待的备份软件可以继续运行。

问题:错误的重定向导致 hook-zombie

这没有按预期发挥作用:备份软件一直等待钩子脚本完成,因为我的重定向有误。虽然我在后台执行命令,并且这些命令本身按预期工作,但命令输出的重定向是由钩子脚本本身完成的。这使得钩子脚本在某个时候像僵尸一样留下来,根本无法完成,因此备份软件无法继续运行。

(
  trap '' HUP INT
  ${cmd_exec} <<< "${cmd}"
) < '/dev/null' > "${PATH_LOCAL_MNT2}/${db_name}" 2> '/dev/null' &

对阵

(
  trap '' HUP INT
  ${cmd_exec} <<< "${cmd}" > "${PATH_LOCAL_MNT2}/${db_name}"
) < '/dev/null' > '/dev/null' 2> '/dev/null' &

我想我现在明白发生了什么:有人只需要实际读取和写入数据作为某种重定向的一部分。至少如果使用 shell 级重定向,而不是命令自己写入文件。我正在使用 SSH,它似乎无法做到这一点,而且似乎完全依赖于 shell 级重定向。

使用上面的第二段代码,我创建了一个在后台执行的子 shell,该子 shell 将 SSH 的输出重定向到某个文件,而父 shell 会与所创建子 shell 的所有输入和输出分离。这种分离实际上使钩子脚本不会变成僵尸,它只是不再需要处理任何重定向。

问题

这是我目前不明白的:重定向到一个文件会导致僵尸 shell 实例负责读取 SSH 输出并将其写入文件。据我所知,确实有一些进程在主动读取 SSH 输出并将其写入文件,不是吗?如果是这样,为什么同一个进程不需要主动读取数据并将其写入/dev/null?这也会导致僵尸留下来,但显然事实并非如此。相反,重定向到到处/dev/null都有记录,以脱离相关通道,如 STDIN、STDOUT 等。

这是为什么?为什么以及如何/dev/null特殊处理重定向,还是我理解的整个重定向过程仍然错误,并且实际上没有 shell 实例主动自行读取和写入数据?

谢谢!

答案1

在我看来你正在重新发明这个nohup工具......


有人只需要实际读取和写入数据作为重定向的一部分。至少如果使用 shell 级重定向,而不是命令自己写入文件,情况就是这样。

一般来说不是,shell 级别重定向(即具体来说<>>>)与程序自行打开文件没有区别:它总是直接从 shell 继承指向已打开文件的文件描述符。

然而,它可能因为它们指定了内联字符串<<<<<所以不是可以打开的文件,因此 shell 子进程必须主动提供数据。这取决于 shell 版本。

据我了解,确实有一些进程正在主动读取 SSH 输出并将其写入文件,不是吗?

如上所述 – 通常没有。所有基于文件的重定向都会直接将子进程的 stdin 或 stdout 文件描述符替换为与指定文件关联的文件描述符。

shell 派生;子进程根据重定向关闭并打开其文件描述符;然后子进程执行(用其自身替换)要运行的命令。如果交互式do_backup命令将其 stdin 和 stdout 附加到其运行的终端的 tty,则将do_backup > log.txt其 stdout 直接附加到log处于写入模式的文件;do_backup > /dev/null以相同的方式处理。

但有一个可能的例外是没有文件,特别是使用<<<<<重定向,其中指定文字字符串。 shell 在这里有几个选择:要么在 /tmp 中创建一个临时文件(具有指定的内容)并附加到进程的标准输入,或者它在子 shell 进程和命令之间使用管道(即转换foo <<< barecho bar | foo)。

旧版本的 Bash 在使用时会创建一个临时文件<<<,但新版本(例如 5.1.16)则改用管道 - 在这种情况下,子 shell 进程必须徘徊直到它将所有内容写入管道(因为管道的缓冲区容量有限)。


请注意,除了文件描述符之外,进程还具有“控制 tty”的概念,即使其 stdin/stdout/stderr 不再指向 tty 设备,它们仍会保持与 tty 设备的连接。此setsid工具可用于将进程与其控制终端分离。

(您也可以使用 systemd 服务管理器工具启动最初未附加的进程systemd-run。)

另外两个与 SIGHUP 相关的技巧是disown在启动后台任务后使用,或者使用& 里面子 shell,即(cmd &)。通常是 shell 本身向后台作业发送 SIGHUP;'disown' 告诉它停止跟踪特定作业,而从子 shell 启动它则首先阻止跟踪它。但是,两者都不能阻止来自其他来源的 SIGHUP。

相关内容