pv
我想在每个管道上运行一系列命令管道。这是一个例子:
for p in 1 2 3
do
cat /dev/zero | pv -N $p | dd of=/dev/null &
done
管道中的实际命令并不重要(cat
/dd
只是一个示例)...
目标是 4 个并发运行的管道,每个管道都有自己的pv
输出。然而,当我尝试将这样的命令置于后台时,pv
会停止,我得到的只是 4 个停止的作业。我试过了{...|pv|...}&
,bash -c "...|pv|..." &
结果都一样。
如何pv
同时运行多个命令管道?
答案1
发现我可以使用xargs
以下-P
选项来做到这一点:
josh@subdivisions:/# seq 1 10 | xargs -P 4 -I {} bash -c "dd if=/dev/zero bs=1024 count=10000000 | pv -c -N {} | dd of=/dev/null"
3: 7.35GiB 0:00:29 [ 280MiB/s] [ <=> ]
1: 7.88GiB 0:00:29 [ 312MiB/s] [ <=> ]
4: 7.83GiB 0:00:29 [ 258MiB/s] [ <=> ]
2: 6.55GiB 0:00:29 [ 238MiB/s] [ <=> ]
发送数组的输出以迭代到xargs
;的 stdin 中要同时运行所有命令,请使用-P 0
答案2
pv
无法在后台启动。
src/main/main.c
正如您在源代码的文件中看到的,他们在终端上pv
设置标志(在结构的中)。他们这样做是为了在不在前台时尝试写入终端时接收到,用信号处理程序捕获它,并将输出重定向到以免“弄乱”终端。TOSTOP
tcsetattr()
c.c_lflag
termios
SIGTTOU
/dev/null
/* * Set terminal option TOSTOP so we get signal SIGTTOU if we try to * write to the terminal while backgrounded. * * Also, save the current terminal attributes for later restoration. */ memset(&t, 0, sizeof(t)); tcgetattr(STDERR_FILENO, &t); t_save = t; t.c_lflag |= TOSTOP; tcsetattr(STDERR_FILENO, TCSANOW, &t);
这当然很恶心,因为它不只是为自己设置该标志,而是为所有使用终端的程序设置该标志。
但这还不是全部。正如 glibc 中所解释的手动的:
函数:int tcsetattr(int filedes, int when, const struct termios *termios-p)
如果从其控制终端上的后台进程调用此函数,通常会向进程组中的所有进程发送 SIGTTOU 信号,就像进程尝试写入终端一样。例外情况是调用进程本身忽略或阻止 SIGTTOU 信号,在这种情况下将执行操作但不会发送信号。请参阅作业控制。
他们没有阻止或忽略SIGTTOU
.而且他们也没有检查tcsetattr()
( 它将返回 -1 并设置errno
为EINTR
如果他们SIGTTOU
之前已经设置了信号处理程序)。
因此该过程被停止。如果它收到一个SIGCONT
(来自bg
命令),它将在尝试完成tcsetattr()
.
所以我想你应该将其视为一项功能;-)