1. 程式人生 > >linux輸出錯誤重定向

linux輸出錯誤重定向

Linux Shell 環境中支援輸入輸出重定向,用符號"<"和">"來表示。0、1和2分別表示標準輸入、標準輸出和標準錯誤資訊輸出,可以用來指定需要重定向的標準輸入或輸出,比如 2>a.txt 表示將錯誤資訊輸出到文件a.txt中。

同時,還可以在這三個標準輸入輸出之間實現重定向,比如將錯誤資訊重定向到標準輸出,可以用 2>&1來實現。 Linux下還有一個非凡的檔案/dev/null,它就像一個無底洞,所有重定向到它的資訊都會消失得無影無蹤。這一點非常有用,當我們不需要回顯程式的所有資訊時,就可以將輸出重定向到/dev/null。 假如想要正常輸出和錯誤資訊都不顯示,則要把標準輸出和標準錯誤都重定向到/dev/null, 例如: # ls 1>/dev/null 2>/dev/null 還有一種做法是將錯誤重定向到標準輸出,然後再重定向到 /dev/null,例如: # ls >/dev/null 2>&1 注重:此處的順序不能更改,否則達不到想要的效果,此時先將標準輸出重定向到 /dev/null,然後將標準錯誤重定向到標準輸出,由於標準輸出已經重定向到了/dev/null,因此標準錯誤也會重定向到/dev/null,於是一切靜靜靜:-) 另外 : make >& make_log 意思是把標準輸出和錯誤輸出都重定向, make>out 只重定向標準輸出!!!!! >log 表示把標準輸出重新定向到檔案log中 >& log 表示把標準輸出和錯誤輸出都定向到檔案log中,相當於 >log 2>&1 地址:
http://blog.sina.com.cn/s/blog_671792fc0100z7j7.html

1、檔案描述符

檔案 檔案描述符 
輸入檔案 0
輸出檔案 1
錯誤輸出檔案 2

2、將標準輸出和錯誤輸出重定向到同一個檔案:2>&1

[email protected]:~/mybash$ cat test.sh 
#/usr/bin/bash
name=kevin
echo $name
ipconfig
[email protected]:~/mybash$ ./test.sh > run.log
./test.sh: line 5: ipconfig:找不到命令
[email protected]:~/mybash$ ./test.sh > run.log 2>&1
[email protected]:~/mybash$ cat run.log 
kevin
./test.sh: line 5: ipconfig:找不到命令
[email protected]:~/mybash$ 

3、將錯誤單獨輸出到檔案

[email protected]:~/mybash$ ./test.sh 2>err.log
kevin
[email protected]:~/mybash$ cat err.log 
./test.sh: line 5: ipconfig:找不到命令
[email protected]:~/mybash$ 

4、將錯誤輸出到系統垃圾箱/dev/null,從而錯誤不出現在命令列介面上

[email protected]:~/mybash$ ./test.sh 2>/dev/null
kevin
[email protected]:~/mybash$ 

Linux Shell 環境中支援輸入輸出重定向,用符號 < 和 > 來表示。0、1和2分別表示標準輸入、標準輸出和標準錯誤資訊輸出,可以用來指定需要重定向的標準輸 入或輸出,比如 2>a.txt 表示將錯誤資訊輸出到檔案a.txt中。

同時,還可以在這三個標準輸入輸出之間實現重定向,比如將 錯誤資訊重定向到標準輸出,可以用 2>&1來實現。

Linux下還有一個特殊的檔案/dev/null,它就像一個無底 洞,所有重定向到它的資訊都會消失得無影無蹤。這一點非常有用,當我們不需要回顯程式的所有資訊時,就可以將輸出重定向到/dev/null。

如 果想要正常輸出和錯誤資訊都不顯示,則要把標準輸出和標準錯誤都重定向到/dev/null, 例如:

# ls 1>/dev/null 2>/dev/null

還有一種做法是將錯誤重定向到標準輸出,然後再重定向到 /dev/null,例如:

# ls >/dev/null 2>&1

注意:此處的順序不能更改,否則 達不到想要的效果,此時先將標準輸出重定向到 /dev/null,然後將標準錯誤重定向到標準輸出,由於標準輸出已經重定向到了/dev/null,因此標準錯誤也會重定向到/dev/null,於 是一切靜悄悄:-)

以上:http://blog.daviesliu.net/2005/08/31/200811/

以下轉自:http://hi.baidu.com/lb_hb/blog/item/bfe6a4659e8877f6f73654bd.html

Chapter 16. I/O 重定向

預設情況下始終有3個"檔案"處於開啟狀態, stdin (鍵盤), stdout (螢幕), and stderr (錯誤訊息輸出到螢幕上). 這3個檔案和其他開啟的檔案都可以被重定向. 對於重定向簡單的解釋就是捕捉一個檔案, 命令, 程式, 指令碼, 或者甚至是指令碼中的程式碼塊(參見 Example 3-1 和 Example 3-2)的輸出, 然後將這些輸出作為輸入傳送到另一個檔案, 命令, 程式, 或指令碼中.

每個開啟的檔案都會被分配一個檔案描述符.[1]stdinstdout, 和stderr的檔案描述符分別是0, 1, 和 2. 對於正在開啟的額外檔案, 保留了描述符3到9. 在某些時候將這些格外的檔案描述符分配給stdin, stdout, 或者是stderr作為臨時的副本連結是非常有用的. [2] 在經過複雜的重定向和重新整理之後需要把它們恢復成正常的樣子 (參見 Example 16-1).

1 COMMAND_OUTPUT >
2 # 重定向stdout到一個檔案.
3 # 如果沒有這個檔案就建立, 否則就覆蓋.

5 ls -lR > dir-tree.list
6 # 建立一個包含目錄樹列表的檔案.

8 : > filename
9 # > 會把檔案"filename"截斷為0長度.
10 # 如果檔案不存在, 那麼就建立一個0長度的檔案(與'touch'的效果相同).
11 # : 是一個佔位符, 不產生任何輸出.
12 
13 > filename 
14 # > 會把檔案"filename"截斷為0長度.
15 # 如果檔案不存在, 那麼就建立一個0長度的檔案(與'touch'的效果相同).
16 # (與上邊的": >"效果相同, 但是在某些shell下可能不能工作.)
17 
18 COMMAND_OUTPUT >>
19 # 重定向stdout到一個檔案.
20 # 如果檔案不存在, 那麼就建立它, 如果存在, 那麼就追加到檔案後邊.
21 
22 
23 # 單行重定向命令(只會影響它們所在的行):
24 # --------------------------------------------------------------------
25 
26 1>filename
27 # 重定向stdout到檔案"filename".
28 1>>filename
29 # 重定向並追加stdout到檔案"filename".
30 2>filename
31 # 重定向stderr到檔案"filename".
32 2>>filename
33 # 重定向並追加stderr到檔案"filename".
34 &>filename
35 # 將stdout和stderr都重定向到檔案"filename".
36 
37 #==============================================================================
38 # 重定向stdout, 一次一行.
39 LOGFILE=script.log
40 
41 echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE
42 echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE
43 echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE
44 echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"."
45 # 每行過後, 這些重定向命令會自動"reset".
46 
47 
48 
49 # 重定向stderr, 一次一行.
50 ERRORFILE=script.errors
51 
52 bad_command1 2>$ERRORFILE # 錯誤訊息發到$ERRORFILE中.
53 bad_command2 2>>$ERRORFILE # 錯誤訊息新增到$ERRORFILE中.
54 bad_command3 # 錯誤訊息echo到stderr,
55 #+ 並且不出現在$ERRORFILE中.
56 # 每行過後, 這些重定向命令也會自動"reset".
57 #==============================================================================
58 
59 
60 
61 2>&1
62 # 重定向stderr到stdout.
63 # 得到的錯誤訊息與stdout一樣, 傳送到一個地方.
64 
65 i>&j
66 # 重定向檔案描述符i 到 j.
67 # 指向i檔案的所有輸出都發送到j中去.
68 
69 >&j
70 # 預設的, 重定向檔案描述符1(stdout)到 j.
71 # 所有傳遞到stdout的輸出都送到j中去.
72 
73 0< FILENAME
74 < FILENAME
75 # 從檔案中接受輸入.
76 # 與">"是成對命令, 並且通常都是結合使用.
77 #
78 # grep search-word <filename
79 
80 
81 [j]<>filename
82 # 為了讀寫"filename", 把檔案"filename"開啟, 並且分配檔案描述符"j"給它.
83 # 如果檔案"filename"不存在, 那麼就建立它.
84 # 如果檔案描述符"j"沒指定, 那預設是fd 0, stdin.
85 #
86 # 這種應用通常是為了寫到一個檔案中指定的地方.
87 echo 1234567890 > File # 寫字串到"File".
88 exec 3<> File # 開啟"File"並且給它分配fd 3.
89 read -n 4 <&3 # 只讀4個字元.
90 echo -n . >&3 # 寫一個小數點.
91 exec 3>&- # 關閉fd 3.
92 cat File # ==> 1234.67890
93 # 隨機儲存.
94 
95 
96 
97 |
98 # 管道.
99 # 通用目的的處理和命令鏈工具.
100 # 與">"很相似, 但是實際上更通用.
101 # 對於想將命令, 指令碼, 檔案和程式串連起來的時候很有用.
102 cat *.txt | sort | uniq > result-file
103 # 對所有的.txt檔案的輸出進行排序, 並且刪除重複行,
104 # 最後將結果儲存到"result-file"中.

可以將輸入輸出重定向和(或)管道的多個例項結合到一起寫在一行上. 1 command < input-file > output-file

3 command1 | command2 | command3 > output-file 參見 Example 12-28 和 Example A-15.

可以將多個輸出流重定向到一個檔案上. 1 ls -yz >> command.log 2>&1
2 # 將錯誤選項"yz"的結果放到檔案"command.log"中.
3 # 因為stderr被重定向到這個檔案中,
4 #+ 所有的錯誤訊息也就都指向那裡了.

6 # 注意, 下邊這個例子就不會給出相同的結果.
7 ls -yz 2>&1 >> command.log
8 # 輸出一個錯誤訊息, 但是並不寫到檔案中.

10 # 如果將stdout和stderr都重定向,
11 #+ 命令的順序會有些不同.

關閉檔案描述符

n<&-

關閉輸入檔案描述符n.

0<&-, <&-

關閉stdin.

n>&-

關閉輸出檔案描述符n.

1>&-, >&-

關閉stdout.

子程序繼承了開啟的檔案描述符. 這就是為什麼管道可以工作. 如果想阻止fd被繼承, 那麼可以關掉它. 1 # 只重定向stderr到一個管道.

3 exec 3>&1 # 儲存當前stdout的"值".
4 ls -l 2>&1 >&3 3>&- | grep bad 3>&- # 對'grep'關閉fd 3(但不關閉'ls').
5 # ^^^^ ^^^^
6 exec 3>&- # 現在對於剩餘的指令碼關閉它.

8 # Thanks, S.C.

如果想了解關於I/O重定向更多的細節參見 附 錄 E.

16.1. 使用exec

exec <filename 命令會將stdin重定向到檔案中. 從這句開始, 後邊的輸入就都來自於這個檔案了, 而不是標準輸入了(通常都是鍵盤輸入). 這樣就提供了一種按行讀取檔案的方法, 並且可以使用sed 和/或 awk來 對每一行進行分析.

Example 16-1. 使用exec重定向標準輸入

1 #!/bin/bash
2 # 使用'exec'重定向標準輸入.


5 exec 6<&0 # 將檔案描述符#6與stdin連結起來.
6 # 儲存了stdin.

8 exec < data-file # stdin被檔案"data-file"所代替.

10 read a1 # 讀取檔案"data-file"的第一行.
11 read a2 # 讀取檔案"data-file"的第二行.
12 
13 echo
14 echo "Following lines read from file."
15 echo "-------------------------------"
16 echo $a1
17 echo $a2
18 
19 echo; echo; echo
20 
21 exec 0<&6 6<&-
22 # 現在將stdin從fd #6中恢復, 因為剛才我們把stdin重定向到#6了,
23 #+ 然後關閉fd #6 ( 6<&- ), 好讓這個描述符繼續被其他程序所使用.
24 #
25 # <&6 6<&- 這麼做也可以.
26 
27 echo -n "Enter data "
28 read b1 # 現在"read"已經恢復正常了, 就是從stdin中讀取.
29 echo "Input read from stdin."
30 echo "----------------------"
31 echo "b1 = $b1"
32 
33 echo
34 
35 exit 0

同樣的, exec >filename 命令將會把stdout重定向到一個指定的檔案中. 這樣所有的命令輸出就都會發向那個指定的檔案, 而不是stdout.

Example 16-2. 使用exec來重定向stdout

1 #!/bin/bash
2 # reassign-stdout.sh

4 LOGFILE=logfile.txt

6 exec 6>&1 # 將fd #6與stdout相連線.
7 # 儲存stdout.

9 exec > $LOGFILE # stdout就被檔案"logfile.txt"所代替了.
10 
11 # ----------------------------------------------------------- #
12 # 在這塊中所有命令的輸出就都發向檔案 $LOGFILE.
13 
14 echo -n "Logfile: "
15 date
16 echo "-------------------------------------"
17 echo
18 
19 echo "Output of \"ls -al\" command"
20 echo
21 ls -al
22 echo; echo
23 echo "Output of \"df\" command"
24 echo
25 df
26 
27 # ----------------------------------------------------------- #
28 
29 exec 1>&6 6>&- # 恢復stdout, 然後關閉檔案描述符#6.
30 
31 echo
32 echo "== stdout now restored to default == "
33 echo
34 ls -al
35 echo
36 
37 exit 0

Example 16-3. 使用exec在同一指令碼中重定向stdin和stdout

1 #!/bin/bash
2 # upperconv.sh
3 # 將一個指定的輸入檔案轉換為大寫.

5 E_FILE_ACCESS=70
6 E_WRONG_ARGS=71

8 if [ ! -r "$1" ] # 判斷指定的輸入檔案是否可讀?
9 then
10 echo "Can't read from input file!"
11 echo "Usage: $0 input-file output-file"
12 exit $E_FILE_ACCESS
13 fi # 即使輸入檔案($1)沒被指定
14 #+ 也還是會以相同的錯誤退出(為什麼?).
15 
16 if [ -z "$2" ]
17 then
18 echo "Need to specify output file."
19 echo "Usage: $0 input-file output-file"
20 exit $E_WRONG_ARGS
21 fi
22 
23 
24 exec 4<&0
25 exec < $1 # 將會從輸入檔案中讀取.
26 
27 exec 7>&1
28 exec > $2 # 將寫到輸出檔案中.
29 # 假設輸出檔案是可寫的(新增檢查?).
30 
31 # -----------------------------------------------
32 cat - | tr a-z A-Z # 轉換為大寫.
33 # ^^^^^ # 從stdin中讀取.Reads from stdin.
34 # ^^^^^^^^^^ # 寫到stdout上.
35 # 然而, stdin和stdout都被重定向了.
36 # -----------------------------------------------
37 
38 exec 1>&7 7>&- # 恢復 stout.
39 exec 0<&4 4<&- # 恢復 stdin.
40 
41 # 恢復之後, 下邊這行程式碼將會如期望的一樣列印到stdout上.
42 echo "File \"$1\" written to \"$2\" as uppercase conversion."
43 
44 exit 0

I/O重定向是一種避免可怕的子 shell中不可存取變數問題的方法.

Example 16-4. 避免子shell

1 #!/bin/bash
2 # avoid-subshell.sh
3 # Matthew Walker提出的建議.

5 Lines=0

7 echo

9 cat myfile.txt | while read line; # (譯者注: 管道會產生子shell)
10 do {
11 echo $line
12 (( Lines++ )); # 增加這個變數的值
13 #+ 但是外部迴圈卻不能存取.
14 # 子shell問題.
15 }
16 done
17 
18 echo "Number of lines read = $Lines" # 0
19 # 錯誤!
20 
21 echo "------------------------"
22 
23 
24 exec 3<> myfile.txt
25 while read line <&3
26 do {
27 echo "$line"
28 (( Lines++ )); # 增加這個變數的值
29 #+ 現在外部迴圈就可以存取了.
30 # 沒有子shell, 現在就沒問題了.
31 }
32 done
33 exec 3>&-
34 
35 echo "Number of lines read = $Lines" # 8
36 
37 echo
38 
39 exit 0
40 
41 # 下邊這些行是指令碼的結果, 指令碼是不會走到這裡的.
42 
43 $ cat myfile.txt
44 
45 Line 1.
46 Line 2.
47 Line 3.
48 Line 4.
49 Line 5.
50 Line 6.
51 Line 7.
52 Line 8. 注意事項: [1]

一個檔案描述符說白了就是檔案系統為了跟蹤這個開啟的檔案而分配給 它的一個數字. 也可以的將其理解為檔案指標的一個簡單版本. 與C中的檔案控制代碼的概念相似.

[2]

使用檔案描述符5可能會引起問題. 當Bash使用exec創 建一個子程序的時候, 子程序會繼承fd5(參見Chet Ramey的歸檔e-mail, SUBJECT: RE: File descriptor 5 is held open). 最好還是不要去招惹這個特定的fd.