首先了解下1和2在Linux中代表什么
在Linux系統中0 1 2是一個文件描述符
名稱 代碼 操作符 Java中表示 Linux 下文件描述符(Debian 為例)
名稱 | 代碼 | 操作符 | Java中表示 | Linux 下文件描述符(Debian 為例) |
標准輸入(stdin) | 0 | < 或 << | System.in | /dev/stdin -> /proc/self/fd/0 -> /dev/pts/0 |
標准輸出(stdout) | 1 | >, >>, 1> 或 1>> | System.out | /dev/stdout -> /proc/self/fd/1 -> /dev/pts/0 |
標准錯誤輸出(stderr) | 2 | 2> 或 2>> | System.err | /dev/stderr -> /proc/self/fd/2 -> /dev/pts/0 |
上面表格引用自這里
編寫一個測試腳本
#!/bin/bash
date #打印當前時間
while true #死循環
do
#每隔2秒打印一次
sleep 2
whatthis #不存在的命令
echo -e "std output" done
當我們啟動腳本時一般命令是 sh test.sh
輸出結果為:(有錯誤的 和正確的日志 同時輸出在了控制台上)
[root@hadoop01 opt]# sh test.sh Thu Jun 11 17:45:36 CST 2020 test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output
一般寫日志輸出 sh test.sh > log.txt 其實 > 就等同於 1>
(控制台的輸出結果):
[root@hadoop01 opt]# sh test.sh > log.txt test.sh: line 7: whatthis: command not found test.sh: line 7: whatthis: command not found test.sh: line 7: whatthis: command not found test.sh: line 7: whatthis: command not found
(log.txt日志文件輸出結果):
[root@hadoop01 opt]# cat log.txt Thu Jun 11 17:47:30 CST 2020 std output std output std output std output
發現 正確的日志輸出在了 log.txt, 錯誤的日志輸出在了控制台上,對應下文的 標准輸出
sh test.sh > log.txt 2>&1
輸出結果為:正確的和錯誤的都同時輸出到了log.txt
[root@hadoop01 opt]# cat log.txt Thu Jun 11 17:53:19 CST 2020 test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output test.sh: line 7: whatthis: command not found std output
sh test.sh 2>&1 1>log.txt
控制台輸出結果為:
[root@hadoop01 opt]# sh test.sh 2>&1 1>log.txt test.sh: line 7: whatthis: command not found test.sh: line 7: whatthis: command not found test.sh: line 7: whatthis: command not found
日志文件輸出結果為:
[root@hadoop01 opt]# cat log.txt Thu Jun 11 17:54:40 CST 2020 std output std output std output
總結
我們總結一下前面的內容:
將 > 符號 理解成指針
默認 錯誤和標准的都輸出到控制台上
> 就是正確輸出的定向
> 就等同於 1>
2> 是錯誤輸出
2>&1 是 將錯誤輸出重定向到正確輸出
組合一下
>log.txt 2>&1 從左往右執行 將正確的輸出結果定向到 log.txt 然后將錯誤的輸出定向到標准輸出的位置 也就是log.txt
所以 錯誤和標准的輸出結果都定向到了 log.txt
2>&1 >log.txt 從左往右執行 將錯誤的輸出定向到標准輸出的位置 也就是控制台然后將正確的標准輸出重定向到log.txt
所以 錯誤的還是在控制台,標准被定向到了log.txt
- 程序運行后會打開三個文件描述符,分別是標准輸入,標准輸出和標准錯誤輸出。
- 在調用腳本時,可使用2>&1來將標准錯誤輸出重定向。
- 只需要查看腳本的錯誤時,可將標准輸出重定向到文件,而標准錯誤會打印在控制台,便於查看。
- >>log.txt會將重定向內容追加到log.txt文件末尾。
- 通過查看/proc/進程id/fd下的內容,可了解進程打開的文件描述符信息。
如何理解
每個程序在運行后,都會至少打開三個文件描述符,分別是0:標准輸入;1:標准輸出;2:標准錯誤。
例如,對於前面的test.sh腳本,我們通過下面的步驟看到它至少打開了三個文件描述符:
./test.sh #運行腳本
ps -ef|grep test.sh #重新打開命令串口,使用ps命令找到test.sh的pid
hyb 5270 4514 0 19:20 pts/7 00:00:00 /bin/bash ./test.sh
hyb 5315 5282 0 19:20 pts/11 00:00:00 grep --color=auto test.sh
可以看到test.sh的pid為5270,進入到相關fd目錄:
cd /proc/5270/fd #進程5270所有打開的文件描述符信息都在此
ls -l #列出目錄下的內容
0 -> /dev/pts/7
1 -> /dev/pts/7
2 -> /dev/pts/7
255 -> /home/hyb/workspaces/shell/test.sh
可以看到,test.sh打開了0,1,2三個文件描述符。同樣的,如果有興趣,也可以查看其他運行進程的文件描述符打開情況,除非關閉了否則都會有這三個文件描述符。
那么現在就容易理解前面的疑問了,2>&1表明將文件描述2(標准錯誤輸出)的內容重定向到文件描述符1(標准輸出),為什么1前面需要&?當沒有&時,1會被認為是一個普通的文件,有&表示重定向的目標不是一個文件,而是一個文件描述符。在前面我們知道,test.sh >log.txt又將文件描述符1的內容重定向到了文件log.txt,那么最終標准錯誤也會重定向到log.txt。我們同樣通過前面的方法,可以看到test.sh進程的文件描述符情況如下:
0 -> /dev/pts/7
1 -> /home/hyb/workspaces/shell/log.txt
2 -> /home/hyb/workspaces/shell/log.txt
255 -> /home/hyb/workspaces/shell/test.sh
我們可以很明顯地看到,文件描述符1和2都指向了log.txt文件,也就得到了我們最終想要的效果:將標准錯誤輸出重定向到文件中。
它們還有兩種等價寫法:
./test.sh >& log.txt
./test.sh &> log.txt
從上表看的出來,我們平時使用的
echo "hello" > t.log
其實也可以寫成
echo "hello" 1> t.log
B.關於2>&1的含義
(關於輸入/輸出重定向本文就不細說了,不懂的可以參考這里,主要是要了解> < << >> <& >& 這6個符號的使用)
含義:將標准錯誤輸出重定向到標准輸出
符號>&是一個整體,不可分開,分開后就不是上述含義了。
比如有些人可能會這么想:2是標准錯誤輸入,1是標准輸出,>是重定向符號,那么"將標准錯誤輸出重定向到標准輸出"是不是就應該寫成"2>1"就行了?是這樣嗎?
如果是嘗試過,你就知道2>1的寫法其實是將標准錯誤輸出重定向到名為"1"的文件里去了
寫成2&>1也是不可以的
C.為什么2>&1要放在后面
考慮如下一條shell命令
nohup java -jar app.jar >log 2>&1 &
(最后一個&表示把條命令放到后台執行,不是本文重點,不懂的可以自行Google)
為什么2>&1一定要寫到>log后面,才表示標准錯誤輸出和標准輸出都定向到log中?
我們不妨把1和2都理解是一個指針,然后來看上面的語句就是這樣的:
本來1----->屏幕 (1指向屏幕)
執行>log后, 1----->log (1指向log)
執行2>&1后, 2----->1 (2指向1,而1指向log,因此2也指向了log)
再來分析下
nohup java -jar app.jar 2>&1 >log &
本來1----->屏幕 (1指向屏幕)
執行2>&1后, 2----->1 (2指向1,而1指向屏幕,因此2也指向了屏幕)
執行>log后, 1----->log (1指向log,2還是指向屏幕)
所以這就不是我們想要的結果。
簡單做個試驗測試下上面的想法:
java代碼如下:
public class Htest { public static void main(String[] args) { System.out.println("out1"); System.err.println("error1"); } }
javac編譯后運行下面指令:
java Htest 2>&1 > log
你會在終端上看到只輸出了"error1",log文件中則只有"out1"
D.每次都寫">log 2>&1"太麻煩,能簡寫嗎?
有以下兩種簡寫方式
&>log
>&log
比如上面小節中的寫法就可以簡寫為:
nohup java -jar app.jar &>log &
上面兩種方式都和">log 2>&1"一個語義。
那么 上面兩種方式中&>和>&有區別嗎?
語義上是沒有任何區別的,但是第一中方式是最佳選擇,一般使用第一種
————————————————
版權聲明:本文為CSDN博主「一個行走的民」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zhaominpro/article/details/82630528