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


10

有没有一种方法可以在没有临时文件的情况下只执行另一个命令?我有一个运行时间更长的命令,另一个具有格式化输出并使用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)


commandA && commandB
eyoung100'3

1
不会传送到的输出,commandAcommandB吗?
约瑟夫(Josef)说恢复莫妮卡(Monica)2015年

如果命令A成功完成,它将启动命令B,这意味着卷曲不会提早开始。
eyoung100

2
@ eyoung100,但是它不会将commandA的stdout传递给commandB的stdin,这是约瑟夫(Josef)所需要的!
roaima

如果要通过输出,则必须使用一个文件。请参阅cuonglm的答案。
eyoung100

Answers:


14

这类似于其他几个答案。如果您具有“ 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以读取其余输出并将其写入管道。


谢谢!所以sponge做那件事我用mbuffer了,但似乎在这里更适合。在这里使用read很聪明。将来一定会记住这一点。
约瑟夫(Josef)说恢复莫妮卡(Monica)2015年

@Josef:我以前从未听说mbuffer过;实际上可能和sponge。我同意使用read是一个聪明的把戏。我不能完全相信它。它会不时弹出整个Stack Exchange(U&L,超级用户,Ask Ubuntu等)的答案。实际上,roaima对这个问题的答案与我的非常相似,只是它不使用sponge(或等效的东西),因此,正如我在评论中提到的那样,它不会commandB像您所需要的那样延迟启动(根据我的理解你的问题)。
G-Man说'恢复莫妮卡'

github.com/ildar-shaimordanov/perl-utils#sponge在bash函数中包装了脚本版本的“ sponge”。
玛丽娜拉

5

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

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

echo A过程替代的目的是什么?
Digital Trauma'3

3
@DigitalTrauma:它可以防止命令替换剥离尾随的换行符。
cuonglm'3

嗯,我知道,是的-很好地抓住了可能的情况
Digital Trauma 2015年

我意识到我的答案不正确,所以我删除了它。我认为这看起来是正确的。+1
Digital Trauma'3

非常感谢!太好了,尤其是回声A / %% A可以保留换行符(即使我不要求这样做也是如此)
约瑟夫说,莫妮卡(

1

我不知道任何可以解决此问题的标准UNIX实用程序。一种选择是用于awk累积commandA输出并将其commandB一次冲洗到

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

请注意,这可能会占用大量内存,因为它awk正在从其输入中构建一个字符串。


1
这剥离了最后一个换行符。假设最后一个字符换行符,则最后需要一个\n或另一个ORS
G-Man说'恢复莫妮卡'

0

您可以使用一个小脚本解决需求。这种特定的变体避免了临时文件和潜在的内存消耗,但以其他过程为代价。

#!/bin/bash
#
IFS= read LINE

if test -n "$LINE"
then
    test -t 2 && echo "Starting $*" >&2
    (
        echo "$LINE"
        cat

    ) | "$@"
else
    exit 0
fi

如果要调用脚本waituntil(并将其设置为可执行文件,然后将其放入PATH等等),则可以像这样使用它

commandA... | waituntil commandB...

( sleep 3 ; date ; id ) | waituntil nl

1
这一切只是延迟启动,commandB直到commandA写入其第一输出为止。那可能还不够好。
G-Man说'恢复莫妮卡'

@ G-Man,但我们不知道。我喜欢你sponge。现在就开始阅读。
roaima
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.