在管道传输到另一个命令之前完全缓冲命令输出?

在管道传输到另一个命令之前完全缓冲命令输出?

有没有办法在没有临时文件的情况下只在另一个命令完成后执行一个命令?我有一个运行时间较长的命令和另一个用于格式化输出并使用curl 将其发送到 HTTP 服务器的命令。如果我只是执行commandA | commandBcommandB将启动curl,连接到服务器并开始发送数据。由于commandA耗时太长,HTTP 服务器会超时。我可以做我想做的事commandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

出于好奇,我想知道是否有办法在没有临时文件的情况下做到这一点。我尝试过mbuffer -m 20M -q -P 100,但卷曲过程仍然从一开始就开始。 Mbuffer 会等待直到commandA实际发送数据完成。 (数据本身最大只有几百 kb)

答案1

这与其他几个答案类似。如果您有“moreutils”软件包,则应该有该sponge命令。尝试

commandA | sponge | { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; }

sponge命令基本上是一个直通过滤器(如cat),只不过它在读取整个输入之前不会开始写入输出。即,它“吸收”数据,然后在挤压时释放数据(就像海绵一样)。因此,在某种程度上,这是“作弊”——如果数据量很大,sponge几乎肯定会使用临时文件。但它对你来说是不可见的;您不必担心诸如选择唯一的文件名以及事后清理之类的家务事情。

读取{ IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; } 的第一行输出sponge。请记住,直到commandA完成  后才会出现。然后它启动commandB,将第一行写入管道,并调用cat读取其余输出并将其写入管道。

答案2

管道中的命令是同时启动的,您需要将commandA输出存储在某个地方以供以后使用。您可以通过使用变量来避免临时文件:

output=$(command A; echo A)
printf '%s' "${output%%A}" | commandB

答案3

我不知道有任何标准 UNIX 实用程序可以解决此问题。一种选择是使用awk累积commandA输出并将其commandB一次性刷新,如下所示

commandA  | awk '{x = x ORS $0}; END{printf "%s", x | "commandB"}'

请注意,这可能会占用大量内存,因为awk是根据输入构建字符串。

答案4

还有一个选择是塔克;

| tac | tac

相关内容