在這個小節中,我們使用一些實際上的練習,透過這些練習可以更加瞭解訊息重導的結果與其運作方式。
我們假設使用帳號 student
登入主機,並使用 /etc/passwd
檔案做為練習範本來源。
指令如下:
student$ mkdir ~/message/; cd ~/message/
student$ cp /etc/passwd ./sample
標準輸入(stdin
)是一個在許多作業系統中,包括 Linux,用於行程之間通信的重要概念。
stdin 主要負責下列工作:
主要輸入通道:stdin 是行程的主要輸入通道,通常預設來自鍵盤輸入。行程可以從 stdin 讀取數據,就像從一個文件或讀取其他數據流一樣。
資料交換接口:stdin 允許行程讀取其他行程的輸出,或者從文件和讀取其他數據來源。這樣可以讓不同的行程互相通信。
stdin 是作業系統和程式設計中的一個核心概念,它支撐了許多資料處理、自動化和跨行程通信的功能。這也是為什麼理解和掌握 stdin 如何工作是每個 Linux 使用者和開發人員的重要技能之一。
來自不同的輸入
我們使用 grep
程式從 sample
檔案中取出 root
字串,可以從下列方法得到結果:
student$ grep root sample
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
上述的做法,是由 grep
程式本身讀取檔案然後進行過濾。
若要使用 stdin
的方法,可以使用下列指令:
student$ grep root < sample
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
我們可以看到處理結果相同,但實際上 grep
並不是直接開啟檔案進行過濾,而是透過 shell 使用 stdin (<
) 將資料提供給 grep
。
在實務的情況下我們更容易看到如下的指令:
student$ cat sample | grep root
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
上述的做法我們稱之為管線重導,grep
也是從 stdin 讀取資料流,而這個資料是由 cat
的輸出轉過去的,grep
在這個案例中沒有開啟任何檔案。有關管線操作的方法我們會在 管線應用 小節中說明。
在這個小節中,我們透過實作來瞭解程式訊息輸出的重新導向,讀者可以在此觀察到輸出的 stdout
與 stderr
的差別,並控制他們的流向。
管理 stdout
執行下列指令
student$ ls -lh sample
得到程式輸出如下
-rw-r--r-- 1 d3admin wheel 1.8K Aug 25 16:44 sample
我們現在知道不管哪一種輸出,都會輸出到預設的終端機,若要分辨是否為一般輸出,則可以使用 >
符號把訊息重導到另一檔案:stdout.txt
。
student$ ls -lh sample > stdout.txt
此時原本會顯示在畫面上的訊息不見了。接著我們再檢視 stdout.txt 內容,如下:
student$ cat stdout.txt
-rw-r--r-- 1 student student 1.8K Aug 25 16:44 sample
由上例所知,透過 ls
列出 sample
物件的結果是一般輸出。
若是我們再多執行幾次,不管如何都會發現 stdout.txt
的內容都只會有一行,原因是 >
會先清空 stdout.txt
,若不存在而對目標路徑又有權限的話,則該檔案會被產生。
要把後續的訊息附加到原檔案而不刪除原有內容的話,可以使用 >>
來處理。
然後透過下列指令刪除原有的 stdout.txt
,然後連做 3 次輸出重導:
student$ rm sample
student$ ls -lh sample >> stdout.txt
student$ ls -lh sample >> stdout.txt
student$ ls -lh sample >> stdout.txt
接著檢視 stdout.txt 內容
student$ cat stdout.txt
-rw-r--r-- 1 student student 1.8K Aug 25 16:44 sample
-rw-r--r-- 1 student student 1.8K Aug 25 16:44 sample
-rw-r--r-- 1 student student 1.8K Aug 25 16:44 sample
管理 stderr
和 stdout
一樣,stderr
也是輸出的一種,但它是錯誤的訊息,所以要重導錯誤輸出的話可以透過代號 2 來進行訊息重導。
我們刻意使用 ls
列出一個不存在的檔案為 sample2
,會得到下列的內容:
student$ ls sample2
ls: cannot access sample2: No such file or directory
透過下列方法,把上面的輸出轉到 stderr.txt:
student$ ls sample2 2> stderr.txt
原本輸出在螢幕上的訊息現在不見了,這些訊息被存到 stderr.txt
裡,使用 cat
來檢視:
student$ cat stderr.txt
ls: cannot access sample2: No such file or directory
與管理 stdout
一樣,若要附加錯誤內容的話,使用 2>>
就可以完成一樣的目標。
使用下列指令先刪除 stderr.txt
檔案後,再連做 3 次:
student$ rm stderr.txt
student$ ls sample2 2>> stderr.txt
student$ ls sample2 2>> stderr.txt
student$ ls sample2 2>> stderr.txt
然後再使用 cat
檢視:
student$ cat stderr.txt
ls: cannot access sample2: No such file or directory
ls: cannot access sample2: No such file or directory
ls: cannot access sample2: No such file or directory
透過以上的練習,我們就可以把訊息進行分類。
經過上一節的練習,大部份都是單一類別的處理,不過在現實的管理作業中我們會有延伸應用,比如想要合併到同一檔案或是在同一指令進行描述,我們將在此進行練習。
因為程式運作中,可能會是 stdout 與 stderr 同時出現,若我們要在同一指令把這些輸出同時指定也是可以的。
使用 ls
列出檔案,如果檔案存在則轉存到 stdout.txt
,若失敗則將訊息存到 stderr.txt
,做法如下:
刪前先前的輸出檔案
student$ rm stdout.xt stderr.txt
刻意列出不存在的檔案 sample2
,並檢視相關檔案
student$ ls sample2 > stdout.txt 2>stderr.txt
顯示 stdout.txt,檔案內容為空。
student$ cat stdout.txt
顯示 stderr.txt,出現錯誤訊息。
student$ cat stderr.txt
ls: cannot access sample2: No such file or directory
若要把所有的訊息,不分類存到 log.txt
檔案中,其作法如下:
student$ ls sample2 > log.txt 2>&1
上述做法能夠把 stderr(2) 轉到 stdout(1),也就是 2>&1
來表示,由於 stdout 指定輸出到 log.txt
,也就是 > log.txt
所表示。最後,不管指令中間的輸出什麼錯誤訊息都可以存到 log.txt
中。
結果如下:
student$ cat log.txt
ls: cannot access sample2: No such file or directory
讓我們把上面的指令修改改為列出 sample
檔案,因為 sample
檔案是在的,所以會經由 > log.txt
輸出,因此內容會是 sample
。
student$ ls sample > log.txt 2>&1
student$ cat log.txt
sample
現在我們已經能夠把所有訊息在同一指令進行分拆或是整併到同一檔案中。同理在某些情境中我們更想要把這些內容以附加的方式進行儲存而不是清空後再存進去,在這種情境裡一樣可以使用 >>
完成。
我們使用 ls
列出檔案,不若其成功或失敗都以附加方式轉存到 log.txt
:
刪除原有的 log.txt
student$ rm log.txt
轉存訊息到 log.txt
student$ ls sample >> log.txt 2>&1
student$ ls sample2 >> log.txt 2>&1
查看檔案
student$ cat log.txt
sample
ls: cannot access sample2: No such file or directory