我知道
program1 | program2
和
program1 | tee outputfile | program2
但有没有办法将program1的输出提供给program2和program3?
答案 0 :(得分:27)
您可以使用tee
执行此操作并处理替换。
program1 | tee >(program2) >(program3)
program1
的输出将通过管道传输到( )
内的任何内容,在这种情况下为program2
和program3
。
答案 1 :(得分:19)
这看似微不足道,但这样做不仅可行,而且还会生成并发或同步进程。
您可能需要注意某些特定效果,例如执行顺序,执行时间等。
这篇文章末尾有一些样本。
由于此问题已被标记 shell和unix,我将首先提供 POSIX兼容的答案。 (对于 bashisms,进一步。)
是的,有一种方法可以使用未命名的管道。
在这个样本中,我将生成100'000个数字的范围,随机化它们并使用4种不同的压缩工具压缩结果来比较压缩比...
为此,我将首先进行准备:
GZIP_CMD=`which gzip`
BZIP2_CMD=`which bzip2`
LZMA_CMD=`which lzma`
XZ_CMD=`which xz`
MD5SUM_CMD=`which md5sum`
SED_CMD=`which sed`
注意:指定命令的完整路径会阻止某些shell解释器(如busybox)运行内置压缩器。并做 方式将确保相同的语法将独立于os安装运行(MacO,Ubuntu,RedHat,HP-Ux之间的路径可能不同......)。
语法NN>&1
(其中NN是3到63之间的数字)确实会生成未命名管道,可以在/dev/fd/NN
找到。 (文件描述符0到2已经为0打开:STDIN,1:STDOUT和2:STDERR)。
(((( seq 1 100000 | shuf | tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 | $GZIP_CMD >/tmp/tst.gz ) 4>&1 | $BZIP2_CMD >/tmp/tst.bz2 ) 5>&1 | $LZMA_CMD >/tmp/tst.lzma ) 6>&1 | $XZ_CMD >/tmp/tst.xz ) 7>&1 | $MD5SUM_CMD
或更具可读性:
GZIP_CMD=`which gzip`
BZIP2_CMD=`which bzip2`
LZMA_CMD=`which lzma`
XZ_CMD=`which xz`
MD5SUM_CMD=`which md5sum`
(
(
(
(
seq 1 100000 |
shuf |
tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 |
$GZIP_CMD >/tmp/tst.gz
) 4>&1 |
$BZIP2_CMD >/tmp/tst.bz2
) 5>&1 |
$LZMA_CMD >/tmp/tst.lzma
) 6>&1 |
$XZ_CMD >/tmp/tst.xz
) 7>&1 |
$MD5SUM_CMD
2e67f6ad33745dc5134767f0954cbdd6 -
当shuf
进行随机展示时,如果您尝试这样做,则必须获得不同的结果,
ls -ltrS /tmp/tst.*
-rw-r--r-- 1 user user 230516 oct 1 22:14 /tmp/tst.bz2
-rw-r--r-- 1 user user 254811 oct 1 22:14 /tmp/tst.lzma
-rw-r--r-- 1 user user 254892 oct 1 22:14 /tmp/tst.xz
-rw-r--r-- 1 user user 275003 oct 1 22:14 /tmp/tst.gz
但您必须能够比较 md5 校验和:
SED_CMD=`which sed`
for chk in gz:$GZIP_CMD bz2:$BZIP2_CMD lzma:$LZMA_CMD xz:$XZ_CMD;do
${chk#*:} -d < /tmp/tst.${chk%:*} |
$MD5SUM_CMD |
$SED_CMD s/-$/tst.${chk%:*}/
done
2e67f6ad33745dc5134767f0954cbdd6 tst.gz
2e67f6ad33745dc5134767f0954cbdd6 tst.bz2
2e67f6ad33745dc5134767f0954cbdd6 tst.lzma
2e67f6ad33745dc5134767f0954cbdd6 tst.xz
使用一些 bashims ,这可能看起来更好,样本使用/dev/fd/{4,5,6,7}
,而不是tee /dev/fd/4 /dev/fd/5 /...
(((( seq 1 100000 | shuf | tee /dev/fd/{4,5,6,7} | gzip >/tmp/tst.gz ) 4>&1 |
bzip2 >/tmp/tst.bz2 ) 5>&1 | lzma >/tmp/tst.lzma ) 6>&1 |
xz >/tmp/tst.xz ) 7>&1 | md5sum
29078875555e113b31bd1ae876937d4b -
也会一样。
这不会创建任何文件,但可以让你比较一个压缩范围的排序整数的大小,在4种不同的压缩工具之间(为了好玩,我使用了4种不同的方式来格式化输出):
(
(
(
(
(
seq 1 100000 |
tee /dev/fd/{4,5,6,7} |
gzip |
wc -c |
sed s/^/gzip:\ \ / >&3
) 4>&1 |
bzip2 |
wc -c |
xargs printf "bzip2: %s\n" >&3
) 5>&1 |
lzma |
wc -c |
perl -pe 's/^/lzma: /' >&3
) 6>&1 |
xz |
wc -c |
awk '{printf "xz: %9s\n",$1}' >&3
) 7>&1 |
wc -c
) 3>&1
gzip: 215157
bzip2: 124009
lzma: 17948
xz: 17992
588895
这演示了如何使用 stdin 和 stdout 重定向到子控件中和在控制台中合并以进行最终输出。
>(...)
和<(...)
最近的bash版本允许使用新的语法功能。
seq 1 100000 | wc -l
100000
seq 1 100000 > >( wc -l )
100000
wc -l < <( seq 1 100000 )
100000
由于|
是未命名管道到/dev/fd/0
,语法<()
会生成临时未命名管道和其他文件描述符/dev/fd/XX
。
md5sum <(zcat /tmp/tst.gz) <(bzcat /tmp/tst.bz2) <(
lzcat /tmp/tst.lzma) <(xzcat /tmp/tst.xz)
29078875555e113b31bd1ae876937d4b /dev/fd/63
29078875555e113b31bd1ae876937d4b /dev/fd/62
29078875555e113b31bd1ae876937d4b /dev/fd/61
29078875555e113b31bd1ae876937d4b /dev/fd/60
这需要安装 GNU file
实用程序。将确定由扩展名或文件类型运行的命令。
for file in /tmp/tst.*;do
cmd=$(which ${file##*.}) || {
cmd=$(file -b --mime-type $file)
cmd=$(which ${cmd#*-})
}
read -a md5 < <($cmd -d <$file|md5sum)
echo $md5 \ $file
done
29078875555e113b31bd1ae876937d4b /tmp/tst.bz2
29078875555e113b31bd1ae876937d4b /tmp/tst.gz
29078875555e113b31bd1ae876937d4b /tmp/tst.lzma
29078875555e113b31bd1ae876937d4b /tmp/tst.xz
这可以通过以下语法执行相同的操作:
seq 1 100000 |
shuf |
tee >(
echo gzip. $( gzip | wc -c )
) >(
echo gzip, $( wc -c < <(gzip))
) >(
gzip | wc -c | sed s/^/gzip:\ \ /
) >(
bzip2 | wc -c | xargs printf "bzip2: %s\n"
) >(
lzma | wc -c | perl -pe 's/^/lzma: /'
) >(
xz | wc -c | awk '{printf "xz: %9s\n",$1}'
) > >(
echo raw: $(wc -c)
) |
xargs printf "%-8s %9d\n"
raw: 588895
xz: 254556
lzma: 254472
bzip2: 231111
gzip: 274867
gzip, 274867
gzip. 274867
注意我使用了不同的方式来计算gzip
压缩计数。
注意由于此操作已同时 ,因此输出顺序将取决于每个命令所需的时间。
如果您运行某些多核或多处理器计算机,请尝试对此进行比较:
i=1
time for file in /tmp/tst.*;do
cmd=$(which ${file##*.}) || {
cmd=$(file -b --mime-type $file)
cmd=$(which ${cmd#*-})
}
read -a md5 < <($cmd -d <$file|md5sum)
echo $((i++)) $md5 \ $file
done |
cat -n
可能会呈现:
1 1 29078875555e113b31bd1ae876937d4b /tmp/tst.bz2
2 2 29078875555e113b31bd1ae876937d4b /tmp/tst.gz
3 3 29078875555e113b31bd1ae876937d4b /tmp/tst.lzma
4 4 29078875555e113b31bd1ae876937d4b /tmp/tst.xz
real 0m0.101s
用这个:
time (
i=1 pids=()
for file in /tmp/tst.*;do
cmd=$(which ${file##*.}) || {
cmd=$(file -b --mime-type $file)
cmd=$(which ${cmd#*-})
}
(
read -a md5 < <($cmd -d <$file|md5sum)
echo $i $md5 \ $file
) & pids+=($!)
((i++))
done
wait ${pids[@]}
) |
cat -n
可以给出:
1 2 29078875555e113b31bd1ae876937d4b /tmp/tst.gz
2 1 29078875555e113b31bd1ae876937d4b /tmp/tst.bz2
3 4 29078875555e113b31bd1ae876937d4b /tmp/tst.xz
4 3 29078875555e113b31bd1ae876937d4b /tmp/tst.lzma
real 0m0.070s
其中排序取决于每个 fork 使用的类型。
答案 2 :(得分:2)
您可以随时尝试将program1的输出保存到文件中,然后将其输入program2和program3输入。
program1 > temp; program2 < temp; program3 < temp;
答案 3 :(得分:2)
bash
手册提到它如何使用命名管道或命名文件描述符模拟>(...)
语法,所以如果你不想依赖bash
,也许你可以做在您的脚本中手动。
mknod FIFO
program3 < FIFO &
program1 | tee FIFO | program2
wait
rm FIFO
答案 4 :(得分:1)
其他答案介绍了这个概念。这是一个实际的演示:
$ echo "Leeroy Jenkins" | tee >(md5sum > out1) >(sha1sum > out2) > out3
$ cat out1
11e001d91e4badcff8fe22aea05a7458 -
$ echo "Leeroy Jenkins" | md5sum
11e001d91e4badcff8fe22aea05a7458 -
$ cat out2
5ed25619ce04b421fab94f57438d6502c66851c1 -
$ echo "Leeroy Jenkins" | sha1sum
5ed25619ce04b421fab94f57438d6502c66851c1 -
$ cat out3
Leeroy Jenkins
当然你可以> /dev/null
而不是out3。
答案 5 :(得分:-1)
使用(;)
语法...尝试ps aux | (head -n 1; tail -n 1)