1. 程式人生 > >Linux tee的花式用法和pee

Linux tee的花式用法和pee

1.tee多重定向

tee [options] FILE1 FILE2 FILE3...

tee的作用是將一份標準輸入多重定向,一份重定向到標準輸出/dev/stdout,然後還將標準輸入重定向到每個檔案FILE中。

例如:

$ cat alpha.log | tee file1 file2 file3 | cat
$ cat alpha.log | tee file1 file2 file3 >/dev/null

上面第一個命令將alpha.log的檔案內容重定向給file{1..3}和標準輸出通過管道傳遞給cat; 上面第二個命令將alpha.log的檔案內容重定向給file{1..3}和/dev/null。

tee重定向給多個命令

寫多了指令碼的人可能遇到過這樣一種需求:將一份標準輸入,重定向到多個命令中去。大概是這樣的:

                      | CMD1
                    ↗
        INPUT | tee 
                    ↘
                      | CMD2

其實bash自身的特性就能實現這樣的需求,通過重定向到子shell中,就能模擬一個檔案重定向行為:

cat alpha.txt | tee >(grep -E "a|b") >(grep -E "d|b|c")

上面的命令將alpha.txt檔案內容重定向為3份:一份給第一個grep命令,一份給第二個grep命令,一份給標準輸出。假如alpha.txt的內容是a b c d e

5個字母分別佔用5行(每行一個字母),上面的輸出結果如下:

a
b
c
d
e  # 前5行是重定向到/dev/stdout的
a
b  # 這2行是重定向給第一個grep後的執行結果
b
c
d  # 這3行是重定向給第二個grep後的執行結果

如果不想要給標準輸出的那份重定向,加上>/dev/null

cat alpha.txt | tee >(grep -E "a|b") >(grep -E "d|b|c") >/dev/null

tee重定向給多個命令時的問題

但是必須注意,tee將資料重定向給不同命令時,這些命令是獨立執行的,它們都會各自開啟一個屬於自己的STDOUT,如果它們都重定向到標準輸出,由於涉及到多個不同的/dev/stdout,它們的結果將出現兩個問題:

  1. 不保證有序性
  2. 因為跨了命令,互動式模式下(預設標準輸出為螢幕)可能會出現命令列隔斷的問題(非互動式下不會有問題)

例如:

$ cat alpha.txt | tee >(grep -E "a|b") >(grep -E "d|b|c") >/dev/null
$ a     # 結果直接出現在提示符所在行
b
b
c
d

$ cat alpha.txt | tee >(grep -E "a|b") >(grep -E "d|b|c") >/dev/null
b
c      # 這次的結果和上次的順序不一樣
d
a
b

這兩個問題,在寫指令碼過程中必須解決。

對於第二個問題:不同/dev/stdout同時輸出時在螢幕上交叉輸出的問題,只需將它們再次重定向走即可,這樣兩份不同的/dev/stdout都再次同時作為一份標準輸入:

$ cat alpha.txt | tee >(grep -E "a|b") >(grep -E "d|b|c") >/dev/null | cat

對於第一個問題:不同/dev/stdout同時輸出時,輸出順序的隨機性,這個沒有好方法,只能在各命令列中將各自的結果儲存到檔案中:

$ cat alpha.txt | tee >(grep -E "a|b" >file1) >(grep -E "d|b|c" >file2) >/dev/null

所以,tee在重定向到多個命令中是有缺陷的,或者說用起來非常不方便,只要將各命令的結果各自儲存時,才能一切按照自己的預期進行。那麼,pee登場了,多重定向非常好用的一個命令。

2.pee代替tee

pee是moreutils包中的一個小工具,先安裝它(epel源中有):

yum -y install moreutils

在man pee中,pee的作用是將標準輸入tee給管道。語法:

pee ["cmds"]

不是很好理解,可以通過幾個示例直接感受它的用法。

$ cat alpha.txt | pee 'grep -E "a|b"' 'grep -E "d|b|c"'
a
b
b
c
d

所以,它的基本用法是pee "CMD1" "CMD2"

如果想將結果儲存到檔案,只需加一個命令即可,例如下面的cat >myfile

$ cat alpha.txt | pee 'grep -E "a|b"' 'grep -E "d|b|c"' 'cat >myfile'

和tee有同樣的問題,如果各命令都沒有指定自己的標準輸出重定向,它們將各自開啟一個屬於自己的/dev/stdout,同樣會有多個/dev/stdout同時輸出時結果資料順序隨機性的問題,但是不會有多個/dev/stdout同時輸出時互動式的隔斷性問題,因為pee會收集各個命令的標準輸出,然後將收集的結果作為自己的標準輸出

pee和tee最大的不同,在於pee將來自多個不同命令的結果作為pee自己的標準輸出,所以下面的命令是可以像普通命令一樣進行重定向的。

INPUT | pee CMD1 CMD2 >/FILE

而tee則不同,是將cmd1和cmd2的結果放進標準輸出(假設各命令自身沒有使用重定向),儲存到FILE中的是tee讀取的標準輸入。

INPUT | tee >(cmd1) >(cmd2) >/FILE

所以,想要重定向tee中cmd1和cmd2的總結果,必須使用額外的管道,或者將整個tee放進子shell。

INPUT | tee >(cmd1) >(cmd2) >/dev/null | cat >FILE1
INPUT | ( tee >(cmd1) >(cmd2) >/dev/null ) >/FILE1