- 論壇徽章:
- 0
|
Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Chapter 16. I/O 重定向
默認(rèn)情況下始終有3個(gè)"文件"處于打開狀態(tài), stdin (鍵盤), stdout (屏幕), and stderr (錯(cuò)誤消息輸出到屏幕上). 這3個(gè)文件和其他打開的文件都可以被重定向. 對于重定向簡單的解釋就是捕捉一個(gè)文件, 命令, 程序, 腳本, 或者甚至是腳本中的代碼塊(參見 Example 3-1 和 Example 3-2)的輸出, 然后將這些輸出作為輸入發(fā)送到另一個(gè)文件, 命令, 程序, 或腳本中.
每個(gè)打開的文件都會(huì)被分配一個(gè)文件描述符.[1]stdin, stdout, 和stderr的文件描述符分別是0, 1, 和 2. 對于正在打開的額外文件, 保留了描述符3到9. 在某些時(shí)候?qū)⑦@些格外的文件描述符分配給stdin, stdout, 或者是stderr作為臨時(shí)的副本鏈接是非常有用的. [2] 在經(jīng)過復(fù)雜的重定向和刷新之后需要把它們恢復(fù)成正常的樣子 (參見 Example 16-1).
1 COMMAND_OUTPUT >
2 # 重定向stdout到一個(gè)文件.
3 # 如果沒有這個(gè)文件就創(chuàng)建, 否則就覆蓋.
4
5 ls -lR > dir-tree.list
6 # 創(chuàng)建一個(gè)包含目錄樹列表的文件.
7
8 : > filename
9 # > 會(huì)把文件"filename"截?cái)酁?長度.
10 # 如果文件不存在, 那么就創(chuàng)建一個(gè)0長度的文件(與'touch'的效果相同).
11 # : 是一個(gè)占位符, 不產(chǎn)生任何輸出.
12
13 > filename
14 # > 會(huì)把文件"filename"截?cái)酁?長度.
15 # 如果文件不存在, 那么就創(chuàng)建一個(gè)0長度的文件(與'touch'的效果相同).
16 # (與上邊的": >"效果相同, 但是在某些shell下可能不能工作.)
17
18 COMMAND_OUTPUT >>
19 # 重定向stdout到一個(gè)文件.
20 # 如果文件不存在, 那么就創(chuàng)建它, 如果存在, 那么就追加到文件后邊.
21
22
23 # 單行重定向命令(只會(huì)影響它們所在的行):
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 # 每行過后, 這些重定向命令會(huì)自動(dòng)"reset".
46
47
48
49 # 重定向stderr, 一次一行.
50 ERRORFILE=script.errors
51
52 bad_command1 2>$ERRORFILE # 錯(cuò)誤消息發(fā)到$ERRORFILE中.
53 bad_command2 2>>$ERRORFILE # 錯(cuò)誤消息添加到$ERRORFILE中.
54 bad_command3 # 錯(cuò)誤消息echo到stderr,
55 #+ 并且不出現(xiàn)在$ERRORFILE中.
56 # 每行過后, 這些重定向命令也會(huì)自動(dòng)"reset".
57 #==============================================================================
58
59
60
61 2>&1
62 # 重定向stderr到stdout.
63 # 得到的錯(cuò)誤消息與stdout一樣, 發(fā)送到一個(gè)地方.
64
65 i>&j
66 # 重定向文件描述符i 到 j.
67 # 指向i文件的所有輸出都發(fā)送到j(luò)中去.
68
69 >&j
70 # 默認(rèn)的, 重定向文件描述符1(stdout)到 j.
71 # 所有傳遞到stdout的輸出都送到j(luò)中去.
72
73 0"是成對命令, 并且通常都是結(jié)合使用.
77 #
78 # grep search-word filename
82 # 為了讀寫"filename", 把文件"filename"打開, 并且分配文件描述符"j"給它.
83 # 如果文件"filename"不存在, 那么就創(chuàng)建它.
84 # 如果文件描述符"j"沒指定, 那默認(rèn)是fd 0, stdin.
85 #
86 # 這種應(yīng)用通常是為了寫到一個(gè)文件中指定的地方.
87 echo 1234567890 > File # 寫字符串到"File".
88 exec 3 File # 打開"File"并且給它分配fd 3.
89 read -n 4 &3 # 寫一個(gè)小數(shù)點(diǎn).
91 exec 3>&- # 關(guān)閉fd 3.
92 cat File # ==> 1234.67890
93 # 隨機(jī)存儲(chǔ).
94
95
96
97 |
98 # 管道.
99 # 通用目的的處理和命令鏈工具.
100 # 與">"很相似, 但是實(shí)際上更通用.
101 # 對于想將命令, 腳本, 文件和程序串連起來的時(shí)候很有用.
102 cat *.txt | sort | uniq > result-file
103 # 對所有的.txt文件的輸出進(jìn)行排序, 并且刪除重復(fù)行,
104 # 最后將結(jié)果保存到"result-file"中.
可以將輸入輸出重定向和(或)管道的多個(gè)實(shí)例結(jié)合到一起寫在一行上. 1 command output-file
2
3 command1 | command2 | command3 > output-file
參見 Example 12-28 和 Example A-15.
可以將多個(gè)輸出流重定向到一個(gè)文件上. 1 ls -yz >> command.log 2>&1
2 # 將錯(cuò)誤選項(xiàng)"yz"的結(jié)果放到文件"command.log"中.
3 # 因?yàn)閟tderr被重定向到這個(gè)文件中,
4 #+ 所有的錯(cuò)誤消息也就都指向那里了.
5
6 # 注意, 下邊這個(gè)例子就不會(huì)給出相同的結(jié)果.
7 ls -yz 2>&1 >> command.log
8 # 輸出一個(gè)錯(cuò)誤消息, 但是并不寫到文件中.
9
10 # 如果將stdout和stderr都重定向,
11 #+ 命令的順序會(huì)有些不同.
關(guān)閉文件描述符
n
0
n>&-
關(guān)閉輸出文件描述符n.
1>&-, >&-
關(guān)閉stdout.
子進(jìn)程繼承了打開的文件描述符. 這就是為什么管道可以工作. 如果想阻止fd被繼承, 那么可以關(guān)掉它. 1 # 只重定向stderr到一個(gè)管道.
2
3 exec 3>&1 # 保存當(dāng)前stdout的"值".
4 ls -l 2>&1 >&3 3>&- | grep bad 3>&- # 對'grep'關(guān)閉fd 3(但不關(guān)閉'ls').
5 # ^^^^ ^^^^
6 exec 3>&- # 現(xiàn)在對于剩余的腳本關(guān)閉它.
7
8 # Thanks, S.C.
如果想了解關(guān)于I/O重定向更多的細(xì)節(jié)參見 附錄 E.
16.1. 使用exec
exec
--------------------------------------------------------------------------------
Example 16-1. 使用exec重定向標(biāo)準(zhǔn)輸入
1 #!/bin/bash
2 # 使用'exec'重定向標(biāo)準(zhǔn)輸入.
3
4
5 exec 6
--------------------------------------------------------------------------------
同樣的, exec >filename 命令將會(huì)把stdout重定向到一個(gè)指定的文件中. 這樣所有的命令輸出就都會(huì)發(fā)向那個(gè)指定的文件, 而不是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 # 在這塊中所有命令的輸出就都發(fā)向文件 $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>&- # 恢復(fù)stdout, 然后關(guān)閉文件描述符#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 # 將一個(gè)指定的輸入文件轉(zhuǎn)換為大寫.
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 #+ 也還是會(huì)以相同的錯(cuò)誤退出(為什么?).
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&1
28 exec > $2 # 將寫到輸出文件中.
29 # 假設(shè)輸出文件是可寫的(添加檢查?).
30
31 # -----------------------------------------------
32 cat - | tr a-z A-Z # 轉(zhuǎn)換為大寫.
33 # ^^^^^ # 從stdin中讀取.Reads from stdin.
34 # ^^^^^^^^^^ # 寫到stdout上.
35 # 然而, stdin和stdout都被重定向了.
36 # -----------------------------------------------
37
38 exec 1>&7 7>&- # 恢復(fù) stout.
39 exec 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; # (譯者注: 管道會(huì)產(chǎn)生子shell)
10 do {
11 echo $line
12 (( Lines++ )); # 增加這個(gè)變量的值
13 #+ 但是外部循環(huán)卻不能存取.
14 # 子shell問題.
15 }
16 done
17
18 echo "Number of lines read = $Lines" # 0
19 # 錯(cuò)誤!
20
21 echo "------------------------"
22
23
24 exec 3 myfile.txt
25 while read line &-
34
35 echo "Number of lines read = $Lines" # 8
36
37 echo
38
39 exit 0
40
41 # 下邊這些行是腳本的結(jié)果, 腳本是不會(huì)走到這里的.
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.
--------------------------------------------------------------------------------
注意事項(xiàng):
[1] 一個(gè)文件描述符說白了就是文件系統(tǒng)為了跟蹤這個(gè)打開的文件而分配給它的一個(gè)數(shù)字. 也可以的將其理解為文件指針的一個(gè)簡單版本. 與C中的文件句柄的概念相似.
[2] 使用文件描述符5可能會(huì)引起問題. 當(dāng)Bash使用exec創(chuàng)建一個(gè)子進(jìn)程的時(shí)候, 子進(jìn)程會(huì)繼承fd5(參見Chet Ramey的歸檔e-mail, SUBJECT: RE: File descriptor 5 is held open). 最好還是不要去招惹這個(gè)特定的fd.
本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u/19919/showart_2170216.html |
|