linux shell下常用輸入輸出操作符是:
1. 标準輸入 (stdin) :代碼為 0 ,使用 < 或 << ; /dev/stdin -> /proc/self/fd/0 0代表:/dev/stdin
2. 标準輸出 (stdout):代碼為 1 ,使用 > 或 >> ; /dev/stdout -> /proc/self/fd/1 1代表:/dev/stdout
3. 标準錯誤輸出(stderr):代碼為 2 ,使用 2> 或 2>> ; /dev/stderr -> /proc/self/fd/2 2代表:/dev/stderr
預設情況下始終有3個"檔案"處于打開狀态, stdin (鍵盤), stdout (螢幕), and stderr (錯誤消息輸出到螢幕上). 這3個檔案和其他打開的檔案都可以被重定向. 對于重定向簡單的解釋就是捕捉一個檔案, 指令, 程式, 腳本, 或者甚至是腳本中的代碼塊(參見 Example 3-1 和 Example 3-2)的輸出, 然後将這些輸出作為輸入發送到另一個檔案, 指令, 程式, 或腳本中.
每個打開的檔案都會被配置設定一個檔案描述符.[1]stdin, stdout, 和stderr的檔案描述符分别是0, 1, 和 2. 對于正在打開的額外檔案, 保留了描述符3到9. 在某些時候将這些格外的檔案描述符配置設定給stdin, stdout, 或者是stderr作為臨時的副本連結是非常有用的. [2] 在經過複雜的重定向和重新整理之後需要把它們恢複成正常的樣子 (參見Example 16-1).
1 COMMAND_OUTPUT >
2 # 重定向stdout到一個檔案.
3 # 如果沒有這個檔案就建立, 否則就覆寫.
4
5 ls -lR > dir-tree.list
6 # 建立一個包含目錄樹清單的檔案.
7
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 # 随機存儲.
|
可以将多個輸出流重定向到一個檔案上.
1 ls -yz >> command.log 2>&1
2 # 将錯誤選項"yz"的結果放到檔案"command.log"中.
3 # 因為stderr被重定向到這個檔案中,
4 #+ 所有的錯誤消息也就都指向那裡了.
5
6 # 注意, 下邊這個例子就不會給出相同的結果.
7 ls -yz 2>&1 >> command.log
8 # 輸出一個錯誤消息, 但是并不寫到檔案中.
9
10 # 如果将stdout和stderr都重定向,
11 #+ 指令的順序會有些不同. |
關閉檔案描述符
n<&- - 關閉輸入檔案描述符n.
0<&-, <&- - 關閉stdin.
n>&- - 關閉輸出檔案描述符n.
1>&-, >&- - 關閉stdout.
子程序繼承了打開的檔案描述符. 這就是為什麼管道可以工作. 如果想阻止fd被繼承, 那麼可以關掉它.
1 # 隻重定向stderr到一個管道.
2
3 exec 3>&1 # 儲存目前stdout的"值".
4 ls -l 2>&1 >&3 3>&- | grep bad 3>&- # 對'grep'關閉fd 3(但不關閉'ls').
5 # ^^^^ ^^^^
6 exec 3>&- # 現在對于剩餘的腳本關閉它.
7
8 # Thanks, S.C. |
exec <filename 指令會将stdin重定向到檔案中. 從這句開始, 後邊的輸入就都來自于這個檔案了, 而不是标準輸入了(通常都是鍵盤輸入). 這樣就提供了一種按行讀取檔案的方法, 并且可以使用sed 和/或 awk來對每一行進行分析.
Example 16-1. 使用exec重定向标準輸入
1 #!/bin/bash
2 # 使用'exec'重定向标準輸入.
3
4
5 exec 6<&0 # 将檔案描述符#6與stdin連結起來.
6 # 儲存了stdin.
7
8 exec < data-file # stdin被檔案"data-file"所代替.
9
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
3
4 LOGFILE=logfile.txt
5
6 exec 6>&1 # 将fd #6與stdout相連接配接.
7 # 儲存stdout.
8
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 # 将一個指定的輸入檔案轉換為大寫.
4
5 E_FILE_ACCESS=70
6 E_WRONG_ARGS=71
7
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提出的建議.
4
5 Lines=0
6
7 echo
8
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. |