不要认为这应该有所作为,但无论如何我都会包括它
GNU bash,版本3.2.51
所以说我有一个包含多个部件的管道,如何防止部分管道在前一部分完成之前运行。
在下面的示例中,我将尝试显示问题
$ echo hello | tee /dev/tty | (echo "Next";sed 's/h/m/' )
输出
Next
hello
mello
用睡眠来显示正在关闭的时间
$ echo hello | tee /dev/tty | (sleep 2;echo "Next";sed 's/h/m/' )
输出
hello
Next
mello
如上所述
hello
Next
mello
但显然这取决于睡眠时间长于上一个命令完成所需的时间,这不是我想要的。
我知道有更好的方法可以做到这一点,但我认为让我了解管道的工作方式对我来说很有教育意义。
尝试了等待和睡眠以及事物的变化,但没有一致的工作。
虽然
仍然打印下一个$ echo hello | tee /dev/tty | sort |(echo "Next";sed 's/h/m/' )
Next
hello
mello
$ echo hello | tee /dev/tty | tac | tac |(echo "Next";sed 's/h/m/' )
Next
hello
mello
如果需要更多信息,请告知我们。
答案 0 :(得分:5)
管道的要点是异步处理数据,以便整体节省时间和空间。如果您想要一个同步管道,您也可以写入文件(如果您需要速度,则在RAM磁盘上)。但是对于接收命令能够以块的形式处理数据的任务,整个流水线可能要慢得多:
a | b | c
最多可以和三个命令中最慢的一样快。a > file; b < file > file2; c < file2
最多可以与每个命令的运行时的总和一样快。因此,如果命令全部在大约N秒内运行(当单独运行时),那么您将查看第一个命令的N的最佳案例运行时和第二个命令的3N。
答案 1 :(得分:2)
bash
中没有语言构造来修改您想要的管道行为。但是,您可以使用命名管道作为二进制信号量的类型:
mkfifo block
echo hello |
{ tee /dev/tty; echo go > block; } |
(read < block; echo "Next"; sed 's/h/m/' )
read
命令阻塞,直到某些内容写入命名管道,直到tee
完成才会发生。
(请注意,这可能不会完全解决您的问题,因为除了进程同步之外,您可能需要处理多个进程写入同一输出文件的事实,并且您没有完全控制各种写入的复用方式(由于缓冲等)
答案 2 :(得分:1)
反向两次技巧按预期工作:由于显而易见的原因,tac(1)
需要在将结果写入输出之前使用整个输入,因此使用tac | tac
可确保管道中的下一个命令不会在上一个命令完成之前开始读取输入。请注意,我说没有开始读取输入,而是没有开始执行。这一点非常重要,你会看到。
这里的问题是你正在调用一个子shell,其中第一个命令不依赖于可用的输入。 echo(1)
不会阻止等待输入,因此这实际上是竞争条件:子shell进程与tee(1)
命令竞争,以查看谁先写入终端。管道根据输入可用性提供同步,如果管道中的某个进程在不依赖于输入可用性的情况下工作,则该进程必然会与管道中的其他进程竞争;你不能阻止它。
要修复它,只有当shell在管道中有可用输入时,才需要以某种方式打印Next
。快速入侵是使用另一个sed(1)
命令执行此操作,该命令用Next\n
替换每行的开头:
echo hello | tee /dev/tty | tac | tac | ( sed -e 's/^/Next\n/' | sed 's/h/m/' )
这有效,但语义并不完全相同:现在,字符串Next\n
是sed 's/h/m/'
输入的一部分。这不是一个问题,因为Next\n
没有出现字母h
,但考虑到这个黑客改变了输入流 - 这可能是也可能不是你的问题具体用例。
答案 3 :(得分:1)
您可以通过使用cat
强制流完成读取,并将结果存储在变量中:
$ echo hello | tee /dev/tty | ( echo before; x="$(cat)"; echo after; sed s/h/m <<<"$x" )
before
hello
after
mello
答案 4 :(得分:0)
这似乎可行,并且不需要命名管道。
$ echo hello | tee >(tac|(echo "Next";sed 's/h/m/')) | cat
hello
Next
mello
我避免使用/ dev / tty,因为它并不总是可用(例如SSH),后跟的“ cat”导致'tee'等待子shell完成。