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")
(實際上這里的兩個>(cmd_list)
不是重定向,而是進程替換。命令行解析開始時,將首先進行進程替換,這兩個grep將等待標准輸入。然后啟動cat和tee,然后tee將標准輸出交給兩個進程的標准輸入)
上面的命令將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,它們的結果將出現兩個問題:
- 不保證有序性
- 因為跨了命令,交互式模式下(默認標准輸出為屏幕)可能會出現命令行隔斷的問題(非交互式下不會有問題)
例如:
$ 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