bugfix> shell > 投稿

ネストされたティーは期待どおりに動作しません。上記の異なるテストを参照してください。

testは小さなファイル(約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が期待どおりに機能しない理由を誰かが説明できますか?

回答 1 件
  • OK:

    | がありません  5行目と15行目。おそらく実行するつもりはなかったでしょう。
    sort -k1 -n tee <(...) <(...) ...  しかし、 sort -k1 -n | tee <(...) <(...) ...

    head  ユーティリティは、最初の行を読み取った後に終了します。存在した後、パイプを閉じ、T出口を「時期尚早」にします。以下を考慮してください。

    $ 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 のデフォルト  パイプへの書き込み時にエラーで終了します。オプション -p   tee になります  パイプへの書き込み時にエラーを無視し、パイプのいずれかが終了した後に続行します。

    以下は、出力で修正されたケース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
    
    

    これが「最速」になると思います(つまり、1つだけの 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 が純粋な運であることを意味します  最後の行になります。

あなたの答え