我认为有些命令会过滤或作用于输入,然后将其作为输出传递给stdout-但有些命令只会采用stdin并对其进行任何处理,而不会输出任何内容。

我对OS X最熟悉,所以马上想到的是pbcopypbpaste,它们是访问系统剪贴板的方式。

无论如何,我知道如果我想要使用stdout并把输出吐到stdout和一个文件,然后可以使用tee命令。而且我对xargs有所了解,但我认为这不是我想要的。

我想知道如何将stdout拆分为两个(或多个)命令。例如:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors


也许有一个比那个更好的例子,但是我真的很想知道如何将stdout发送到不中继它的命令。并且在保持stdout不被“静音”的同时-我不是在问如何cat文件和grep的一部分并将其复制到剪贴板上-具体命令不是那么重要。

也-我没有问如何将其发送到文件中,这可能是一个“重复”的问题(对不起),但我做了一些查找,只能找到类似的问题,询问如何在stdout和文件之间进行拆分-这些问题的答案似乎是stdout,我认为这对我不起作用。

最后,您可能会问:“为什么不使pbcopy成为管道链中的最后一件事? ”我的回答是1)如果我想使用它并且仍然在控制台中看到输出怎么办? 2)如果我要使用两个在处理输入后不输出tee的命令怎么办?

哦,还有一件事-我意识到我可以使用stdout和命名管道(tee),但是我希望有一种方法可以简洁地内联完成,而无需事先设置:)

评论

unix.stackexchange.com/questions/26964/的可能重复项...

#1 楼

您可以使用tee并对此进行处理替换:

cat file.txt | tee >(pbcopy) | grep errors


这会将cat file.txt的所有输出发送到pbcopy,并且只会在您的计算机上获取grep的结果控制台。

您可以在tee部分中放置多个进程:

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors


评论


与pbcopy无关,但通常值得一提:在原始输入之后的下一个管道段中也可以看到任何过程替换输出;例如:seq 3 |三通>(cat -n)| cat -e(cat -n编号输入行,cat -e用$标记换行符;您会看到cat -e应用于原始输入(第一个),然后又应用到了cat -n的输出)。多个过程替代的输出将以不确定的顺序到达。

–mklement0
2014-12-9 4:47



>(仅在bash中有效。如果您尝试使用例如sh则无效,请务必注意这一点。

–AAlvz
2014-12-16 16:16



@AAlvz:重点是:进程替换不是POSIX功能;在Ubuntu上充当sh的破折号不支持它,甚至当以sh调用或set -o posix生效时,甚至Bash本身也会停用该功能。但是,不仅仅Bash支持进程替换:ksh和zsh也支持它们(不确定其他)。

–mklement0
2015年4月15日在2:59



@ mklement0似乎不正确。在zsh(Ubuntu 14.04)上,行打印为:1 1 2 2 3 3 1 $ 2 $ 3 $令人遗憾的是,因为我真的希望功能能够像您所说的那样。

–阿克套
16-10-21在9:56

@Aktau:确实,我的示例命令仅按照bash和ksh中的描述工作-zsh显然不会通过管道发送来自输出过程替换的输出(可以说,这是更好的选择,因为它不会污染发送到下一个管道的内容段-尽管它仍然可以打印)。但是,在所有提到的shell中,最好不要使用单个管道将常规的stdout输出和来自进程替换的输出混合在一起-输出顺序将是不可预测的,其方式可能很少出现或以较大的方式出现输出数据集。

–mklement0
16-10-22在5:39

#2 楼

您可以为tee指定多个文件名,此外,可以将标准输出传递到一个命令中。要将输出分派给多个命令,您需要创建多个管道并将每个管道指定为tee的一个输出。有几种方法可以执行此操作。

进程替换

如果您的shell是ksh93,bash或zsh,则可以使用进程替换。这是一种将管道传递给需要文件名的命令的方法。外壳程序创建管道,并将文件名/dev/fd/3传递给命令。该数字是管道连接到的文件描述符。某些unix变体不支持/dev/fd;在这些上,使用命名管道代替(请参见下文)。

tee >(command1) >(command2) | command3


文件描述符

在任何POSIX Shell中,您可以使用多个文件描述符。这需要一个支持/dev/fd的unix变体,因为tee的输出(除一个输出外)都必须通过名称指定。

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1


命名管道

最基本和可移植的方法是使用命名管道。缺点是您需要找到一个可写目录,创建管道,然后进行清理。

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2


评论


非常感谢您为不想依赖bash或特定ksh的用户提供了两个替代版本。

–trr
13年7月2日在4:08

tee“ $ tmp_dir / f1”“ $ tmp_dir / f2” | command3当然应该是command3 | tee“ $ tmp_dir / f1”“ $ tmp_dir / f2”,因为您希望将command3的stdout通过管道传输到tee,不是吗?我无限期地在破折号和三通处测试了您的版本,等待输入,但是切换顺序产生了预期的结果。

– AdrianGünter
18年4月10日在19:21

@AdrianGünter否。这三个示例均从标准输入中读取数据,并将其发送至command2和command3中的每个。

–吉尔斯'所以-不再是邪恶的'
18年4月10日在22:04

我看到@Gilles,我误解了意图,并试图错误地使用该代码段。感谢您的澄清!

– AdrianGünter
18年4月10日在23:18

如果您对所使用的shell没有控制权,但是可以显式使用bash,则可以执行 |。 bash -c'tee>(命令1)>(命令2)| command3'。就我而言,这很有帮助。

– gc5
18-10-13在18:09

#3 楼

只需使用进程替换即可。

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)


grep是两个二进制文件,它们从mycommand_exec的输出与其特定于进程的输入相同。

#4 楼

如果您使用的是zsh,则可以利用MULTIOS功能的强大功能,即完全摆脱tee命令:

uname >file1 >file2


只需将uname的输出写入两个不同的文件:file1file2,相当于uname | tee file1 >file2

标准输入的类似重定向

wc -l <file1 <file2


相当于cat file1 file2 | wc -l(请注意(这与wc -l file1 file2不同,后者将分别计算每个文件中的行数)。

当然,您也可以使用MULTIOS使用进程替换将输出重定向到文件而不是其他进程,例如:

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')


评论


很高兴知道。 MULTIOS是默认情况下处于启用状态的选项(可以使用unsetopt MULTIOS关闭该选项)。

–mklement0
15年4月15日在3:39

#5 楼

对于命令产生的较小输出,我们可以将输出重定向到临时文件,然后将这些临时文件循环发送到命令。当执行的命令顺序可能很重要时,这可能很有用。例如,以下脚本可以做到这一点:

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"


测试运行在Ubuntu 16.04上,将/bin/sh作为dash shell:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash


#6 楼

将命令STDOUT捕获到一个变量中,然后根据需要多次使用它:

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy


如果还需要捕获STDERR,则最后使用2>&1命令的格式,如下所示:

commandoutput="$(command-to-run 2>&1)"


评论


变量存储在哪里?如果您要处理的是大文件或类似文件,这是否会占用大量内存?变量的大小是否受到限制?

– cwd
2012年1月7日4:09

如果$ commandoutput很大怎么办?最好使用管道和进程替换。

– Nikhil Mulley
2012年1月7日13:00

显然,只有当您知道输出的大小很容易容纳在内存中并且可以在运行下一个命令之前先缓冲整个输出时,这种解决方案才可行。管道通过允许任意长度的数据并将其实时流传输到接收器来解决这两个问题。

–trr
2013年6月23日12:15



如果您的输出很小,并且知道输出将是文本而不是二进制,那么这是一个很好的解决方案。 (shell变量通常不是二进制安全的)

– Rucent88
2014年7月20日下午4:48

我无法使用它处理二进制数据。我认为回声试图解释空字节或其他一些非字符数据。

–罗夫
17年5月21日在16:13

#7 楼

moreutils软件包中还有pee。它是为它设计的:
pee 'command1' 'command2' 'cat -'

#8 楼

这可能是有用的:http://www.spinellis.gr/sw/dgsh/(有向图外壳)
好像是bash替代品,支持“ multipipe”命令的更简单语法。

#9 楼

这是一个快速而又肮脏的部分解决方案,它与包括busybox在内的任何外壳兼容。

它解决的更狭窄的问题是:将完整的stdout打印到一个控制台上,并在另一个控制台上进行过滤,而无需临时进行文件或命名管道。


启动到同一主机的另一个会话。要查找其TTY名称,请键入tty。假设/dev/pty/2
在第一个会话中,运行the_program | tee /dev/pty/2 | grep ImportantLog:


您将获得一个完整的日志和一个经过过滤的日志。

#10 楼

对此的另一种尝试:
$ cat file.txt | tee >(head -1 1>&2) | grep foo

通过将tee的文件参数重定向到bash的进程替换进行工作,其中此进程为head,它仅打印一行(标题),并将其自身的输出重定向到stderr(在使其可见)。