从脚本运行时后台作业终止,但从 bash 提示符运行时运行良好

从脚本运行时后台作业终止,但从 bash 提示符运行时运行良好

我使用的是 MacOS 10.9.4。我想将 sftp 置于后台,以便稍后自动通过命名管道将一些文件推送给它,正如建议的那样这里。当我从 bash 提示符手动输入命令时,它工作正常(cat为简单起见,使用监听器作业):

    $ mkfifo test
    $ cat > test &
    [1] 60765
    $ cat test | cat &
    [2] 60781 60782
    [1]  + 60765 suspended (tty input)  cat > test
    $ echo works! > test
    works!
    $ ps -ax | grep 60765
    60765 ttys023    0:00.00 cat
    60900 ttys023    0:00.00 grep 60765

但是,当我将其放入 Bash 脚本中时,它停止工作:

    $ cat test.sh
    mkfifo test1
    cat > test1 &
    echo $!
    cat test1 | cat &
    $ bash test.sh
    60847
    $ echo fails > test1
    ^C%
    $ ps -ax | grep 60847
    60882 ttys023    0:00.00 grep 60847

据我了解,这里的问题是,cat > test1 &从提示符运行时该行工作正常,但从脚本运行时以某种方式终止,所以我的监听器作业也会收到 EOF 并终止。

我在这里遗漏了什么?我怎样才能通过脚本让它工作?

编辑:我面临的实际问题是这样的。为了开发,我必须将代码部署到远程服务器。为此,我使用了 rsync,为了稍微自动化一下,我使用了样本监听文件夹中的文件变化并在发生变化时运行 rsync。

    $ fswatch -0 . | while read -d "" event;
      do
        rsync ./ {remote folder}
      done

在我尝试在具有较大延迟的慢速连接上使用它之前,它一直运行良好。Rsync 每次都会打开新的 ssh 连接并查找文件差异,这在慢速连接上需要时间。我试图通过打开与 sftp 的持久连接并只向其推送更改的文件(我从 fswatch 收到的名称)来解决这个问题。为了实现这一点,我需要一种方法来启动 sftp 进程并在 fswatch 事件发生时向其发送命令。我发现问题,但写入 /proc/{pid}/fd/0 不应该在 Mac 上工作,所以我尝试使用命名管道来回答。我可以cat > test1 &在启动 fswatch 脚本之前手动运行,所以这实际上会起作用。但我想要一个可靠的解决方案,以便能够将此脚本提供给我的同事。

答案1

所有这些都与后台进程尝试从终端读取时发生的情况有关。默认情况下,只有活动进程组才允许从终端读取。如果活动进程组之外的进程尝试从终端读取,则会发送信号以暂停该进程,直到它被 shell 唤醒。

在第一个示例中,您启动了两个进程组。每个进程组都在后台启动,因此不允许任何进程组从终端读取数据。

cat > test &将尝试立即从终端读取并暂停。但是,您只会在bash显示下一个提示之前收到通知,因此您必须键入另一个命令才能收到通知。

您的echo命令写入第二个进程组(未暂停)正在读取的管道。在该序列结束时,第一个cat命令保持暂停状态,并且从未再次被唤醒。

在您的第二个示例中,整个脚本在单个进程组中运行。因此此时您cat在一个进程组中有三个不同的命令,其中一个在从终端读取时被阻止。然后您返回到初始bashshell,因此必须暂停该线程组。

此外,从初始 shell 的角度来看bash,该线程组已经终止,因为它看到了bash您输入的第二个命令:terminate。初始bashshell 不知道子进程产生了孙进程,并且其中一个孙进程被阻止从终端读取。

当脚本的进程组不再受初始bashshell 控制时,第一个cat命令将在其输入中获得 EOF。此时所有cat命令都将看到一个空输入并立即完成处理。

相关内容