stdin, stdout, stderr以及重定向

stdin, stdout, stderr以及重定向

做者:Sam(甄峯sam_code@hotmail.comhtml

 

stdin, stdout,stderr: standard I/O streamslinux

 

介紹:ios

在一般狀況下,UNIX每一個程序在開始運行的時刻,都會有3個已經打開的stream. 分別用來輸入,輸出,打印診斷和錯誤信息。一般他們會被鏈接到用戶終端(tty(4). 但也能夠改變到其它文件或設備。這取決於雙親進程的選擇和設置。shell

 

3symbols都是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

2stderr)重定向到1stdout. 但又將stdout(1) 丟棄。

因此即爲丟棄stdoutstderr. 

stdin, stdout, stderr 詳解

NAME

stdin, stdout, stderr - 標準 I/O 

SYNOPSIS

#include <stdio.h>       extern FILE *stdin;       extern FILE *stdout;       extern FILE *stderr;

DESCRIPTION

一般,每一個 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 關聯的整數形式的文件描述符分別是 01 還有
      2。預處理器符號 STDIN_FILENOSTDOUT_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)

CONSIDERATIONS

錯誤流 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

16. I/O重定向

目錄
16.1. 使用 exec
16.2. 代碼塊重定向
16.3. 重定向的應用

默認狀況下始終有3"文件"處於打開狀態,stdin(鍵盤),stdout(屏幕),stderr(錯誤消息輸出到屏幕上).3個文件和其餘打開的文件均可以被重定向.對於重定向簡單的解釋就是捕捉一個文件,命令, 程序,腳本, 或者是腳本中的代碼塊(請參考例子3-1例子3-2)的輸出,而後將這些輸出做爲輸入發送到另外一個文件,命令, 程序,或腳本中.

每一個打開的文件都會被分配一個文件描述符.[1] stdin, stdout,stderr的文件描述符分別是0, 1, 2. 除了這3個文件,對於其餘那些須要打開的文件,保留了文件描述符39.在某些狀況下,將這些額外的文件描述符分配給stdin,stdout, stderr做爲臨時的副本連接是很是有用的.[2] 在通過複雜的重定向和刷新以後須要把它們恢復成正常狀態(請參考例子16-1).

 1  COMMAND_OUTPUT >  2  # stdout重定向到一個文件.   3  # 若是這個文件不存在, 那就建立, 不然就覆蓋.     5  ls -lR > dir-tree.list  6  # 建立一個包含目錄樹列表的文件.     8  : > filename  9  # >操做, 將會把文件"filename"變爲一個空文件(就是size0).   10  # 若是文件不存在, 那麼就建立一個0長度的文件('touch'的效果相同).   11  # :是一個佔位符, 不產生任何輸出.   12   13  > filename   14  # >操做, 將會把文件"filename"變爲一個空文件(就是size0).   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  # stdoutstderr都重定向到文件"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  # 重定向stderrstdout.   72  # 將錯誤消息的輸出, 發送到與標準輸出所指向的地方.   73   74  i>&j  75  # 重定向文件描述符ij.   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    3 command1 | command2 | command3 > output-file

請參考例子12-28例子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 # 若是將stdoutstderr都重定向,   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 # 感謝, S.C. 

若是想了解關於I/O重定向更多的細節,請參考Appendix E.

注意事項

[1]

一個文件描述符說白了就是文件系統爲了跟蹤這個打開的文件而分配給它的一個數字. 也能夠的將其理解爲文件指針的一個簡單版本. C語言中文件句柄的概念很類似.

[2]

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

Linux Tips
IO Redirection

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 tty(1) command. Note, the (1) after command names in UNIX refers to the section of the man pages that the documentation for the command exists in.

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 '$' is your prompt. Note also, you can always specify a complete path name for a file.

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, 2>&1 means send STDERR to the same place that STDOUT is going to.

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
相關文章
相關標籤/搜索