我使用的是 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
在一个进程组中有三个不同的命令,其中一个在从终端读取时被阻止。然后您返回到初始bash
shell,因此必须暂停该线程组。
此外,从初始 shell 的角度来看bash
,该线程组已经终止,因为它看到了bash
您输入的第二个命令:terminate。初始bash
shell 不知道子进程产生了孙进程,并且其中一个孙进程被阻止从终端读取。
当脚本的进程组不再受初始bash
shell 控制时,第一个cat
命令将在其输入中获得 EOF。此时所有cat
命令都将看到一个空输入并立即完成处理。