做者:Sam(甄峯) sam_code@hotmail.comhtml
stdin, stdout,stderr: standard I/O streamslinux
介紹:ios
在一般狀況下,UNIX每一個程序在開始運行的時刻,都會有3個已經打開的stream. 分別用來輸入,輸出,打印診斷和錯誤信息。一般他們會被鏈接到用戶終端(tty(4)). 但也能夠改變到其它文件或設備。這取決於雙親進程的選擇和設置。shell
這3個symbols都是stdio(3) macro,類型爲指向FILE的指針。能夠被fprintf() fread()等函數使用。ubuntu
當一個程序開始啓動後,stdin, stdout, and stderr are 0, 1,and 2,其它的文件描述符則排在其後。緩存
Linux的本質就是一切皆文件,輸入輸出設備也是以文件形式存在和管理的。app
內核啓動的時候默認打開這三個I/O設備文件:標準輸入文件stdin,標準輸出文件stdout,標準錯誤輸出文件stderr,分別獲得文件描述符 0, 1, 2。
stderr是不緩存的,stdout是行間緩存的。請注意:less
因此:ide
for(i = 0; i < 10; i++)
{
fprintf(stdout, "This is stdout[%d]", i);
fprintf(stderr, "This is stderr[%d]", i);
}函數
會所有顯示stderr以後,再顯示stdout.
又由於stdout是行內緩存,因此加 \n 後會馬上顯示。
在程序中使用stdin,stdout,stderr,能夠先:
extern FILE *stdout;
extern FILE *stderr;
重定向:
在實際應用中,能夠分別使用stdout,stderr來輸出。
以後再重定向:
如:
./example 1 > /dev/null
這樣就將 stdout(1) 的輸出丟棄。只顯示stderr.
./example 2 > /dev/null
這樣就將 stderr(2) 的輸出丟棄。只顯示stdout.
./example >> /dev/null 2>&1
將2(stderr)重定向到1(stdout). 但又將stdout(1) 丟棄。
因此即爲丟棄stdout和stderr.
stdin, stdout, stderr - 標準 I/O 流
#include <stdio.h> extern FILE *stdin; extern FILE *stdout; extern FILE *stderr;
一般,每一個 Unix 程序在啓動時都會打開三個流,一個用於輸入,一個用於輸出,一個用於打印診斷或錯誤消息。典型的,他們被鏈接到用戶的終端 (參見 tty(4)) 可是也有可能指向文件或是其餘設備,取決於父進程選擇設置了什麼 (參見 sh(1) 的重定向 (``Redirection'') 章節。) 輸入流被稱爲 ``standard input''; 輸出流被稱爲 ``standard output''; 錯誤流被稱爲 ``standard error''。這些名詞一般簡寫爲符號,用於引用這些文件,它們是 stdin, stdout, 和 stderr. 這些符號中,每個都是 stdio(3) 中的一個宏,類型是指向 FILE 的指針,能夠用於相似 fprintf(3) 或 fread(3) 等函數中。 因爲 FILE 是一個對 Unix 文件描述符加以緩衝的包裝,下層的文件也可使用- 始的 Unix 文件接口來存取。也就是,相似 read(2) 和 lseek(2) 的函數。與流 stdin, stdout, 和 stderr 關聯的整數形式的文件描述符分別是 0,1 還有 2。預處理器符號 STDIN_FILENO,STDOUT_FILENO 和 STDERR_FILENO 分別以它們爲值,定義在 <unistd.h> 中。 注意混合使用 FILE 和- 始的文件描述符可能帶來不可預料的結果,通常應當避免。(對於喜歡追根問底的人:POSIX.1 規範的 8.2.3 節詳細地描述了這樣的混合使用怎樣才能不出錯。) 一個簡單的規則是,文件描述符由內核控制,而 stdio 僅僅是一個庫。它的意思是,例如當調用 exec 以後,子進程能夠繼承全部打開的文件描述符,可是任何雜械牧鞫疾豢稍俅嬡×恕 因爲符號 stdin, stdout, 和 stderr 被指定爲宏,爲它們賦值將致使不可移植。利用庫函數 freopen(3) ,標準流能夠用來指向不一樣的文件。引進這個函數專門用來爲 stdin, stdout, 和 stderr 從新賦值。標準流在調用 exit(3) 和程序正常停止時被關閉。 sh(1), csh(1), open(2), fopen(3), stdio(3)
錯誤流 stderr 是非緩衝的。輸出流 stdout 是行緩衝的,若是它指向一個終端。不徹底的行只有在調用 fflush(3) 或 exit(3) ,或者打印了新行符以後纔會顯示。這樣可能帶來沒法預料的結果,尤爲是調試輸出時。標準流 (或任何其餘流) 的緩衝模式能夠用函數 setbuf(3) 或 setvbuf(3) 來切換。注意當 stdin 與一個終端關聯時,也許終端驅動中存在輸入緩衝,與 stdio 緩衝徹底無關。(確實如此,通常的終端輸入在內核中是行緩衝的。) 內核對輸入的控制能夠經過對 tcsetattr(3) 的調用來修改,參見 stty(1), 和 termios(3) 。 宏 stdin, stdout, 和 stderr 遵 ANSI X3.159-1989 (``ANSI C'') 標準,這個標準同時規定了這三個流應當在程序啓動時打開。
Advanced Bash-Script-3.9.1_cn
默認狀況下始終有3個"文件"處於打開狀態,stdin(鍵盤),stdout(屏幕),和stderr(錯誤消息輸出到屏幕上).這3個文件和其餘打開的文件均可以被重定向.對於重定向簡單的解釋就是捕捉一個文件,命令, 程序,腳本, 或者是腳本中的代碼塊(請參考例子3-1和例子3-2)的輸出,而後將這些輸出做爲輸入發送到另外一個文件,命令, 程序,或腳本中.
每一個打開的文件都會被分配一個文件描述符.[1] stdin, stdout,和stderr的文件描述符分別是0, 1, 和 2. 除了這3個文件,對於其餘那些須要打開的文件,保留了文件描述符3到9.在某些狀況下,將這些額外的文件描述符分配給stdin,stdout, 或stderr做爲臨時的副本連接是很是有用的.[2] 在通過複雜的重定向和刷新以後須要把它們恢復成正常狀態(請參考例子16-1).
1 COMMAND_OUTPUT > 2 # 將stdout重定向到一個文件. 3 # 若是這個文件不存在, 那就建立, 不然就覆蓋. 4 5 ls -lR > dir-tree.list 6 # 建立一個包含目錄樹列表的文件. 7 8 : > filename 9 # >操做, 將會把文件"filename"變爲一個空文件(就是size爲0). 10 # 若是文件不存在, 那麼就建立一個0長度的文件(與'touch'的效果相同). 11 # :是一個佔位符, 不產生任何輸出. 12 13 > filename 14 # >操做, 將會把文件"filename"變爲一個空文件(就是size爲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 M>N 38 # "M"是一個文件描述符, 若是沒有明確指定的話默認爲1. 39 # "N"是一個文件名. 40 # 文件描述符"M"被重定向到文件"N". 41 M>&N 42 # "M"是一個文件描述符, 若是沒有明確指定的話默認爲1. 43 # "N"是另外一個文件描述符. 44 45 #============================================================================== 46 47 # 重定向stdout, 一次一行. 48 LOGFILE=script.log 49 50 echo "This statement is sent to the log file, \"$LOGFILE\"." 1>$LOGFILE 51 echo "This statement is appended to \"$LOGFILE\"." 1>>$LOGFILE 52 echo "This statement is also appended to \"$LOGFILE\"." 1>>$LOGFILE 53 echo "This statement is echoed to stdout, and will not appear in \"$LOGFILE\"." 54 # 每行事後, 這些重定向命令會自動"reset". 55 56 57 58 # 重定向stderr, 一次一行. 59 ERRORFILE=script.errors 60 61 bad_command1 2>$ERRORFILE # Error message sent to $ERRORFILE. 62 bad_command2 2>>$ERRORFILE # Error message appended to $ERRORFILE. 63 bad_command3 # Error message echoed to stderr, 64 #+ and does not appear in $ERRORFILE. 65 # 每行事後, 這些重定向命令也會自動"reset". 66 #============================================================================== 67 68 69 70 2>&1 71 # 重定向stderr到stdout. 72 # 將錯誤消息的輸出, 發送到與標準輸出所指向的地方. 73 74 i>&j 75 # 重定向文件描述符i到j. 76 # 指向i文件的全部輸出都發送到j. 77 78 >&j 79 # 默認的, 重定向文件描述符1(stdout)到j. 80 # 全部傳遞到stdout的輸出都送到j中去. 81 82 0< FILENAME 83 < FILENAME 84 # 從文件中接受輸入. 85 # 與">"是成對命令, 而且一般都是結合使用. 86 # 87 # grep search-word <filename 88 89 90 [j]<>filename 91 # 爲了讀寫"filename", 把文件"filename"打開, 而且將文件描述符"j"分配給它. 92 # 若是文件"filename"不存在, 那麼就建立它. 93 # 若是文件描述符"j"沒指定, 那默認是fd 0, stdin. 94 # 95 # 這種應用一般是爲了寫到一個文件中指定的地方. 96 echo 1234567890 > File # 寫字符串到"File". 97 exec 3<> File # 打開"File"而且將fd 3分配給它. 98 read -n 4 <&3 # 只讀取4個字符. 99 echo -n . >&3 # 寫一個小數點. 100 exec 3>&- # 關閉fd 3. 101 cat File # ==> 1234.67890 102 # 隨機訪問. 103 104 105 106 | 107 # 管道. 108 # 通用目的處理和命令鏈工具. 109 # 與">", 很類似, 可是實際上更通用. 110 # 對於想將命令, 腳本, 文件和程序串連起來的時候頗有用. 111 cat *.txt | sort | uniq > result-file 112 # 對全部.txt文件的輸出進行排序, 而且刪除重複行. 113 # 最後將結果保存到"result-file"中. |
能夠將輸入輸出重定向和(或)管道的多個實例結合到一塊兒寫在同一行上.
1 command < input-file > output-file 2 3 command1 | command2 | command3 > output-file |
能夠將多個輸出流重定向到一個文件上.
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 #+ 命令的順序會有些不一樣. |
子進程繼承了打開的文件描述符.這就是爲何管道能夠工做.若是想阻止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 # 感謝, S.C. |
若是想了解關於I/O重定向更多的細節,請參考Appendix E.
一個文件描述符說白了就是文件系統爲了跟蹤這個打開的文件而分配給它的一個數字. 也能夠的將其理解爲文件指針的一個簡單版本. 與C語言中文件句柄的概念很類似. |
|
使用文件描述符5可能會引發問題. 當Bash使用exec建立一個子進程的時候, 子進程會繼承fd5(參考Chet Ramey的歸檔e-mail, SUBJECT: RE: File descriptor 5 is held open). 最好仍是不要去招惹這個特定的fd. |
http://www.linuxsa.org.au/tips/io-redirection.html
UNIX had the concept of IO redirection long before DOS copied and bastardised the concept. The UNIX IO redirection concept is fundamental to many of the things that you can do with UNIX, and it is quite a well-developed idea, so we will explore this concept here.
Why do I mention UNIX at all? Well, Linux is a UNIX operating system!
Under UNIX, all programs that run are given three open files when they are started by a shell:
0. |
Standard in, or STDIN. This is where input comes from, and it normally points at your terminal device. To find out what device is your terminal, use the You can arrange to run any command and pass it input from a file in the following way: $ some-command < /path/to/some/file Note, the ' For example: $ grep -i Fred < /etc/passwd Would search for the string 'fred' in /etc/passwd, regardless of the case of the characters. But wait a minute, you object, I always use: $ grep -i Fred /etc/passwd This is true, but you can also pass the file in on STDIN, and you will get different results if you do. Can you see what the difference is? |
1. |
Standard out, or STDOUT. This is where the normal output from a program goes. It normally points at your terminal as well, but you can redirect it. You can redirect output in the following way: $ some-program > /path/to/some/file For example: $ grep -i Fred /etc/passwd > /tmp/results |
2. |
Standard error, or STDERR. This is where error output from your program goes. This normally points at your terminal as well, but you can redirect it. Why have different output places for standard out and standard error? Well, as you will see when you come to writing shell scripts, you often do not want error messages cluttering up the normal output from a program. |
You will forgive me for starting the above list at 0, I am sure, when you learn that each of these IO 'channels' are represented by small numbers, called file descripters (FDs), that have exactly those numbers. That is, STDIN is FD 0, while STDOUT is FD 1, and STDERR is FD 2.
When the shell runs a program for you, it opens STDIN as FD 0, STDOUT as FD 1, and STDERR as FD 2, and then runs the program (technically, it almost always does a fork(2)
and then anexec(3)
or one of the exec?? calls). If you have redirected one of STDIN, STDOUT or STDERR, your shell opens that file as the appropriate FD before running the program.
Now, what does this all have to do with you, I hear you ask?
Well, there are lots of neat things you can do, but some things to watch out for as well.
A lot of inexperienced UNIX users assume that they can redirect a file into a program and use the same name for redirecting the output:
$ some-program < mega-important-data-file > mega-important-data-file
They become very upset after doing the above, especially if that mega-important data file has never been backed up anywhere. Why is this?
The shell opens the mega-important-data-file for reading and associates it with FD 0 (or STDIN), and then opens it for writing, but truncates it to zero length, and associates it with FD 1 (or STDOUT) as well.
So, if you want to do something like the above, use a different file name for the output file. Oh, you should also back up files as well :-).
Now, there are lots of redirection symbols that you can use, and here are some of them:
< file |
means open a file for reading and associate with STDIN. |
<< token |
Means use the current input stream as STDIN for the program until token is seen. We will ignore this one until we get to scripting. |
> file |
means open a file for writing and truncate it and associate it with STDOUT. |
>> file |
means open a file for writing and seek to the end and associate it with STDOUT. This is how you append to a file using a redirect. |
n>&m |
means redirect FD n to the same places as FD m. Eg, |
OK, here are some tricks that you might want to use in various places.
If you are gathering evidence for a bug report, you might want to redirect the output from a series of programs to a text file (never mind that you can use the script command to do the same :-). So you might do the following:
$ some-buggy-program > important-evidence.txt $ echo '---------MARKER-------' >> important-evidence.txt $ some-buggy-program some-params >> important-evidence.txt
The second and subsequent lines append the output from the commands issues to the evidence file rather than overwriting them. Try the following:
$ echo This is a line of text > /tmp/file.txt $ echo This is another line > /tmp/file.txt
What do you get?
Now try:
$ echo This is a line of text > /tmp/file.txt $ echo This is another line >> /tmp/file.txt
What do you get this time?
OK, for the last few tricks here. Sometimes you want to append STDOUT and STDERR to a file. How do you do it?
$ some-command >> /tmp/log.log 2>&1
The 2>&1
says make STDERR point to the same places as STDOUT. Since STDOUT is open already, and the shell has done a seek to the end, STDERR will also be appended to STDOUT.
If you want to append a line to a file, you can echo the line you want with a redirect, rather than firing up an editor:
$ echo Some text >> /path/to/some/file
It turns out that you can cause the shell to redirect to other file descriptors as well, and if you look in the configure scripts that come with many UNIX software packages, you will see examples of this.
Why is redirecting so important? Well, it is used in many shell scripts, it is a simple and conventient mechanism to sending output to any file without the programmer having to add code for handling command line instructions, and it is the UNIX way of doing things :-).
It is also the same as piping, where you redirect output to, or input from, a pipe device. The pipe device has a process living on the other side, but we will look at this later.
Regards
------- Richard Sharpe, sharpe@ns.aus.com, Master Linux Administrator :-), Samba (Team member, www.samba.org), Ethereal (Team member, www.zing.org) Co-author, SAMS Teach Yourself Samba in 24 Hours Author: First Australian 5-day, intensive, hands-on Linux SysAdmin course