【Shell 教學】Redirect 讓人暈頭轉向的「重導向」 (1/2)

使用情境

當我們要把我們跑程式的各種輸出結果存下來,或把文件當成輸入(就是懶得自己打字或複製貼上),就會需要用到重導向(redirect)。

觀念

對於終端機(terminal)來說,檔案描述符(File descriptor)的白話意思是
「可以被當成檔案的介面」,一般有三種:

  • 0: stdin —— 標準輸入,簡單說就是你打給電腦的字
  • 1: stdout —— 標準輸出,簡單說就是電腦打給你的字(顯示在螢幕上的)
  • 2: stderr —— 標準錯誤,雖然也是顯示在螢幕上,但是是遇到錯誤遇到 bug 顯示的錯誤訊息

為什麼要這樣區分呢?輸入輸出很直覺,就是你跟電腦來往兩個方向的訊息。
然而之所以有個「stderr」(也是這邊複雜化的開始⋯⋯),就是因為要方便工程師和電腦內部作業與紀錄而產生的額外介面。

用現實的例子來舉例的話,可能比較像是假如我們去餐廳 ——

  • stdin 是你跟服務生(電腦的終端機就像是一位服務生)說的話
  • stdout 是服務生跟你說的話
  • stderr 是餐廳內部出包了,店長或主管出來跟你道歉的內容 (?

但由於直接跟你接洽的只有服務生本人,因此「輸入」只有一種;
而上層與餐廳內部的消息服務生只是傳話給你,跟服務生本身講出來的話還是有點差異的,所以「輸出」會分成「標準」和「錯誤」兩種。

其實這裡比喻沒有很恰當,
但還是希望大家能理解「正常的結果」和「錯誤的紀錄」是有差異的這件事⋯⋯

運算子(operator)與實際用法 —— 基礎篇

白話說就是「符號」啦!

> —— 把左邊指令的東西導到哪裡去

這個還算蠻直觀的,不廢話直接上例子

假設我們有一個 Python 檔 print10.py

# --- filename: print10.py
for i in range(10):
    print(i)

接下來在 terminal 打

python print10.py

上面的程式碼會跑出 1 ~ 10 的數字

1
2
3
4
5
6
7
8
9
10

那如果要把這些東西存成 result.txt 的檔案內容,就只需要在 terminal 的指令上加上 > 並在接著檔名即可,也就是

python print10.py > result.txt
#                 ^^^^^^^^^^^^ 這樣就可以囉~
# 請注意!因為輸出結果存到檔案裡了,所以螢幕上不會有東西!

< —— 讓電腦幫我自動打字

好,既然可以不用自己手動複製螢幕顯示的東西出來(到可能你的報告上?),那可不可以不用自己手動貼上到 terminal 去呢?可以!
一樣舉個簡單但無聊的例子,假設我們有個查詢用的 Python 檔:

# --- filename: age_lookup.py
database: dict = {
    "Jeff":   25.255,
    "Kaiwei": 24.485,
    "Eric":   25.474,
}

for i in range(10):  # 假設要查詢 10 次
    user_input = input("Name? ")
    age_output = database[user_input]
    print(f"{user_input} is {age_output} years old!")

原本要自己去慢慢打

$ python age_lookup.py
Name? Jeff
Jeff is 25.255 years old!
Name? Kaiwei
Kaiwei is 24.485 years old!
Name? Eric
Eric is 25.474 years old!
Name? Eric
Eric is 25.474 years old!
Name? 

但如果有一個檔案( inputfile.txt )已經存好要查詢的人名了

Jeff
Kaiwei
Eric
Eric
Kaiwei
Kaiwei
Jeff
Jeff
Eric
Kaiwei

那我們可以直接

$ python age_lookup.py < inputfile.txt
Name? Jeff is 25.255 years old!
Name? Kaiwei is 24.485 years old!
Name? Eric is 25.474 years old!
Name? Eric is 25.474 years old!
Name? Kaiwei is 24.485 years old!
Name? Kaiwei is 24.485 years old!
Name? Jeff is 25.255 years old!
Name? Jeff is 25.255 years old!
Name? Eric is 25.474 years old!
Name? Kaiwei is 24.485 years old!

甚至結合「 > 」把結果存下來,完全自動化!

python age_lookup.py < inputfile.txt > result.txt
Name? Jeff is 25.255 years old!
Name? Kaiwei is 24.485 years old!
Name? Eric is 25.474 years old!
Name? Eric is 25.474 years old!
Name? Kaiwei is 24.485 years old!
Name? Kaiwei is 24.485 years old!
Name? Jeff is 25.255 years old!
Name? Jeff is 25.255 years old!
Name? Eric is 25.474 years old!
Name? Kaiwei is 24.485 years old!

以上就是 result.txt 的內容~而且不會佔滿螢幕,也不會因為輸入輸出文字太多讓 terminal 當機!

接下來下一篇會講解如果上面的 code 遇到 bug 時的狀況,以及很複雜很刺激(也很好用)的 >&&> 的用法(也是比較難懂的部分)~敬請期待 ?

看完這篇可以接續閱讀這篇,我們將會講解更完整的 redirection 的內容唷~

https://weikaiwei.com/linux/redirect-2/

寫在後面的小作業

為了給予各位練習的機會,在這邊出幾題小作業讓大家嘗試看看

  1. 寫一份 shell script 把現在的時間存下來(提示: date 指令)
  2. 寫一份 shell script 去紀錄 Python 執行的時間(提示: time 指令)

參考資料

留言討論區