首页 > 解决方案 > 使用嵌套三通不会产生好的结果

问题描述

嵌套三通没有按预期工作,请参阅上面的不同测试:

测试是一个小文件(大约 550 kb):

$ >test
$ for _I in `seq 1 100000`; do
        echo "$_I" >> test
done
$ wc -l test
100000 test

这是一些带有嵌套 tee 的代码:

$ cat test.sh
echo "1 :"
cat "$1" |\
 tee >(paste -sd+ | bc -l)\
     >(wc -l)\
     >(sort -k1 -n\
    tee >(uniq -c | wc -l)\
        >(uniq -c | awk '{ print $1 }' | paste -sd+ | bc -l)\
            >(tail -n 1)\
            >(head -n 1) &>/dev/null) &>/dev/null
sleep 20
echo "2:"
cat "$1" |\
 tee >(paste -sd+ | bc -l)\
     >(wc -l)\
     >(sort -k1 -n | uniq -c\
    tee >(wc -l)\
            >(awk '{ print $1 }' | paste -sd+ | bc -l)\
     >(sort -k1 -n |\
    tee >(tail -n 1)\
            >(head -n 1) &>/dev/null) &>/dev/null) &>/dev/null
sleep 20
echo "3:"
cat "$1" |\
 tee >(paste -sd+ | bc -l)\
     >(wc -l)\
     >(sort -k1 -n | uniq -c | wc -l)\
     >(sort -k1 -n | uniq -c | awk '{ print $1 }' | paste -sd+ | bc -l)\
     >(sort -k1 -n | tail -n 1)\
     >(sort -k1 -n | head -n 1) &>/dev/null
sleep 20

这产生了这个结果:

1 :
0
14139
99962730
2:
0
100000
5000050000
3:
1
100000
100000
100000
100000
5000050000

这是一些没有任何 tee 重定向的代码:

$ cat test2.sh 
echo "4 :"
echo "sum : `cat $1 | paste -sd+ | bc -l`"
echo "count numbers : `cat $1 | wc -l`"
echo "count uniq : `cat $1 | sort -k1 -n| uniq -c | wc -l`"
echo "uniq sort and sum : `cat $1 | sort -k1 -n | uniq -c | awk '{ print $1 }' | paste -sd+ | bc -l`"
echo "max : `cat $1 | sort -k1 -n | tail -n 1`"
echo "min : `cat $1 | sort -k1 -n | head -n 1`"

以下是供参考的结果:

$ ./test2.sh test
4 :
sum : 5000050000
count numbers : 100000
count uniq : 100000
uniq sort and sum : 100000
max : 100000
min : 1

有人可以解释一下为什么 test.sh 中的案例 1 和 2 不能按预期工作吗?

标签: shelltee

解决方案


好的:

  • |在第 5 行和第 15 行丢失了。您可能不是有意执行
    sort -k1 -n tee <(...) <(...) ...sort -k1 -n | tee <(...) <(...) ...
  • head实用程序在读取第一行后退出。在它存在之后,它会关闭管道,这使得 tee 退出“过早”。考虑以下:

$ seq 10000 | tee >(head -n1) >(tail -n1) >/dev/null
1
10000    # fine
$ seq 100000 | tee >(head -n1) >(tail -n1) >/dev/null
1
14139  # erroro, this is not ok. tail should have outputted 100000 here. Why doesn't it?
$ seq 100000 | tee >(sponge | head -n1 ) >(tail -n1) >/dev/null
100000   # sponge fixes it
1
$ seq 100000 | tee --output-error=warn >(head -n1) >(tail -n1) >/dev/nul
1
tee: /dev/fd/63: Broken pipe  # head exists which closes pipe and that makes tee exit here
10000000
# seq 10000000 | tee -p >(head -n1) >(tail -n1) >/dev/null
1
10000000  # works as expected

的默认值tee是在写入任何管道时出错时退出。选项在写入管道时忽略错误,并在任何管道退出后继续-ptee

以下是使用输出固定的案例 1(缩进不同,但相同):

cat "$1" | tee -p >/dev/null \
    >(paste -sd+ | bc -l) \
    >(wc -l) \
    >(sort -k1 -n | tee -p >/dev/null \
            >(uniq -c | wc -l) \
            >(uniq -c | awk '{ print $1 }' | paste -sd+ | bc -l) \
            >(tail -n 1) \
            >(head -n1) \
    )

1
100000
100000
100000
100000
5000050000

这是案例 2 固定:

cat "$1" | tee -p >/dev/null \
        >(paste -sd+ | bc -l) \
        >(wc -l) \
        >(sort -k1 -n | uniq -c | tee -p >/dev/null \
                >(wc -l) \
                >(awk '{ print $1 }' | paste -sd+ | bc -l) \
                >(sort -k1 -n | tee -p >/dev/null \
                        >(tail -n 1) \
                        >(head -n1)
                )
        )

      1 1
      1 99999
100000
100000
100000
5000050000

我想这将是“最快的”(即uniq -c与案例 1 相比只有一个电话),我想这可能是您对案例 2 的意思:

cat "$1" | tee >/dev/null \
        >(paste -sd+ | bc -l) \
        >(wc -l) \
        >(sort -k1 -n | tee -p >/dev/null \
                >(uniq -c | tee -p >/dev/null \
                        >(wc -l) \
                        >(awk '{ print $1 }' | paste -sd+ | bc -l) \
                ) \
                >(tail -n 1) \
                >(head -n1)
        )

它的输出与案例 1 相同。

作为旁注,这些进程替换和tee命令的输出是不同步的。我的意思是输出的顺序实际上是随机的,并且该行5000050000到达最后一行是纯粹的运气。


推荐阅读