????????在Linux中,重定向(Redirection)是一種強大的功能,允許用戶控制命令的輸入來源(
stdin
)和輸出目標(stdout
和stderr
)。通過重定向,你可以將命令的輸出保存到文件、從文件讀取輸入,甚至將錯誤信息與正常輸出分離處理。
目錄
一、重定向的原理
1、輸出重定向原理
2、追加重定向原理
3、輸入重定向原理
4、標準輸出流和標準錯誤流雖然都顯示在屏幕上,但二者有什么區別呢?
1. 標準輸出 (stdout) 和標準錯誤輸出 (stderr)
2.?perror("perror")?
函數原型
參數的作用
二、系統調用 dup2
功能說明
返回值
使用注意事項
應用示例
一、重定向的原理
????????理解了文件描述符的概念及其分配規則后,我們就能掌握重定向的原理。通過以下三個示例,你會發現重定向的本質就是改變文件描述符所指向的struct file*對象。
1、輸出重定向原理
所謂輸出重定向,就是將程序原本要輸出到某個文件的數據,轉而輸出到另一個指定文件中。
????????例如,若需將原本輸出到顯示器(文件描述符1)的數據重定向至log.txt文件,可在打開log.txt前先關閉文件描述符1。這樣后續打開log.txt時,系統會自動為其分配文件描述符1,實現輸出重定向:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(1);int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0){perror("open");return 1;}printf("hello world\n");printf("hello world\n");printf("hello world\n");printf("hello world\n");printf("hello world\n");fflush(stdout);close(fd);return 0;
}
運行結果后,我們發現顯示器上并沒有輸出數據,對應數據輸出到了log.txt文件當中:
補充說明:
? ? printf
函數默認將數據輸出到標準輸出(stdout)。stdout本質上是一個指向struct FILE
結構體的指針(對應下面紅色方框部分),該結構體包含一個文件描述符成員變量。
????????對于stdout而言,這個文件描述符的值固定為1。因此,printf
實際上是將數據輸出到文件描述符為1的設備。
????????需要注意的是,C語言的數據并不會立即寫入操作系統內存,而是先存儲在C語言維護的緩沖區中。因此,在使用printf
打印數據后,通常需要調用fflush
函數來強制刷新緩沖區,確保數據被寫入目標文件。
2、追加重定向原理
????????追加重定向和輸出重定向的唯一區別就是,輸出重定向是覆蓋式輸出數據,而追加重定向是追加式輸出數據。
????????比如,我們希望將原本輸出到"顯示器文件"的數據追加寫入log.txt文件,可以先關閉文件描述符1,再以追加寫入模式打開log.txt。這樣就能實現將數據追加到log.txt的重定向操作:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(1);int fd = open("log.txt", O_WRONLY|O_APPEND|O_CREAT, 0666);if(fd < 0){perror("open");return 1;}printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");printf("hello Linux\n");fflush(stdout);close(fd);return 0;
}
運行結果后,我們發現對應數據便追加式輸出到了log.txt文件當中:
3、輸入重定向原理
輸入重定向就是,將我們本應該從一個文件讀取數據,現在重定向為從另一個文件讀取數據。
????????例如,若要讓原本從"鍵盤文件"讀取數據的scanf函數改為從log.txt文件讀取,可以在打開log.txt前先關閉文件描述符為0的文件(即"鍵盤文件")。這樣后續打開log.txt時,系統會自動為其分配文件描述符0。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{close(0);int fd = open("log.txt", O_RDONLY | O_CREAT, 0666);if (fd < 0){perror("open");return 1;}char str[40];while (scanf("%s", str) != EOF){printf("%s\n", str);}close(fd);return 0;
}
代碼解析
-
關閉標準輸入:
close(0)
?關閉了文件描述符0(標準輸入)。 -
打開文件:
open("log.txt", O_RDONLY | O_CREAT, 0666)
?嘗試以只讀模式打開文件log.txt
,如果文件不存在則創建。由于文件描述符0已被關閉,新打開的文件會占用最小的可用文件描述符,即0。 -
讀取輸入:
scanf("%s", str)
?從文件描述符0(現在是log.txt
)讀取數據,直到遇到文件結束符(EOF)。 -
輸出內容:
printf("%s\n", str)
?將讀取到的內容打印到標準輸出。
scanf
?按空格分隔讀取:
-
第一次讀取?
"hello"
,打印?hello
。 -
第二次讀取?
"world"
,打印?world
。 -
第三次讀取?
"hello"
,打印?hello
。 -
第四次讀取?
"world"
,打印?world
。 -
...
-
直到遇到?
"hello Linux"
?時,scanf
?會先讀?"hello"
,再讀?"Linux"
。?
運行結果后,我們發現scanf函數將log.txt文件當中的數據都讀取出來了:?
說明一下:
????????scanf函數是默認從stdin讀取數據的,而stdin指向的FILE結構體中存儲的文件描述符是0,因此scanf實際上就是向文件描述符為0的文件讀取數據。?
4、標準輸出流和標準錯誤流雖然都顯示在屏幕上,但二者有什么區別呢?
請看以下示例代碼,它分別向標準輸出流和標準錯誤流打印了兩行字符串:
#include <stdio.h>
int main()
{printf("hello printf\n"); //stdoutperror("perror"); //stderrfprintf(stdout, "stdout:hello fprintf\n"); //stdoutfprintf(stderr, "stderr:hello fprintf\n"); //stderrreturn 0;
}
1. 標準輸出 (stdout
) 和標準錯誤輸出 (stderr
)
-
stdout
(標準輸出):默認輸出到終端(屏幕),通常用于正常程序輸出。 -
stderr
(標準錯誤輸出):默認也輸出到終端(屏幕),但專用于錯誤或警告信息,不受重定向影響。
2.?
perror("perror")
?
函數原型
void perror(const char *s);
-
參數?
s
:用戶提供的字符串,會作為錯誤消息的前綴。 -
功能:打印當前?
errno
?對應的系統錯誤描述,格式為?s: 錯誤描述
。 -
作用:向?
stderr
?輸出錯誤信息。 -
特點:
-
perror
?用于打印最近的系統錯誤信息(通過?errno
?獲取)。 -
如果之前沒有發生錯誤,可能輸出?
"perror: Success"
。 -
始終輸出到?
stderr
。
-
參數的作用
-
如果?
s
?非空,perror
?會先輸出?s
,后跟冒號和空格,再輸出系統錯誤信息。 -
如果?
s
?是空字符串 (""
) 或?NULL
,則只輸出系統錯誤信息(無前綴)。
直接運行程序,結果很顯然就是在顯示器上輸出四行字符串:
????????從表面上看,標準輸出流和標準錯誤流似乎沒有區別,都會將數據顯示在屏幕上。但當我們嘗試將程序運行結果重定向到log.txt文件時,就會發現不同之處:只有標準輸出的兩行內容被寫入文件,而標準錯誤的兩行信息仍然顯示在屏幕上:
重點:
????????實際上,重定向操作針對的是文件描述符1(標準輸出流),并不會影響文件描述符2(標準錯誤流)!!!!!!
二、系統調用 dup2
????????實現重定向只需復制fd_array數組中的元素。比如,當我們將fd_array[3]的內容復制到fd_array[1]時,由于C語言中stdout對應文件描述符1,這樣輸出就被重定向到了log.txt文件。
????????在Linux操作系統中提供了系統接口dup2,我們日常開發常常使用dup2系統調用來復制文件描述符,完成重定向。dup2的函數原型如下:?
int dup2(int oldfd, int newfd);
功能說明
????????dup2函數將fd_array[oldfd]的內容復制到fd_array[newfd]中。若newfd文件描述符已打開,函數會先將其關閉。
返回值
????????成功時返回newfd,失敗時返回-1。
使用注意事項
- 當oldfd為無效文件描述符時,調用失敗且不會關閉newfd對應的文件
- 若oldfd有效且newfd等于oldfd,函數直接返回newfd而不執行任何操作
應用示例
????????將log.txt的文件描述符fd和標準輸出描述符1傳入dup2后,fd_array[fd]內容會復制到fd_array[1]。由于標準輸出(stdout)對應文件描述符1,原本輸出到顯示器的內容將被重定向至log.txt文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);if (fd < 0){perror("open");return 1;}close(1);dup2(fd, 1);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;
}
代碼運行后,我們即可發現數據被輸出到了log.txt文件當中: