shell script 是我們在解決一些工作不可或缺的利器.
而在 Debian/ Ubuntu 的環境下, 預設的 /bin/sh 指向到 dash. 一個嚴格遵守 POSIX標準的 shell.
以往的 bash 環境下, 若要同時 redirect stdout/ stderr 到 /dev/null 裡, 不顯示任何結果的指令很簡單, 可參考下列程式
#!/bin/sh ooxx() { echo "Hello stdout" echo "Hello stderr" 1>&2 } ooxx &> /dev/null
這邊要注意的是
0: 代表 stdin
1: 代表 stdout
2: 代表 stderr
只要使用 &> /dev/null 即可將 stdout/ stderr 重新導向 /dev/null, 因此執行 script 不會有任何輸出. 都被 /dev/null 吃掉了~ 如下圖
但其實這樣的寫法不符合 POSIX 標準. 我們來看一下, 同樣的指令, 如果在 dash 環境下執行會發生什麼事
可以看到 dash 不會將 stdout/ stderr 導到 /dev/null. 而是全部印出來.
好, 所以我們改寫一下程式碼, 用更嚴謹的寫法來做重新導入的動作
#!/bin/sh ooxx() { echo "Hello stdout" echo "Hello stderr" 1>&2 } ooxx 2>&1 > /dev/null
上述程式先將 stderr 導入 stdout, 接著再將這些結果導入 /dev/null.
note: 這邊要注意一下, 如果要導入的接收方是 “fd", 要在前面加 “&", 否則 shell 會將結果輸出到檔案 “1″, 而不是代表 stdout 的 fd: 1
執行結果如下:
這種寫法還是只能將 stdout 導到 /dev/null, 為什麼 stderr 不會一併進去呢 ? 我們不是已經做了導入的動作了嗎 ?
為了確認不是 dash 的問題, 所以換成 bash 重新執行一次程式.
結果發現不論 dash or bash 都會有相同的問題 – 2>&1 > /dev/null 只會把 stdout 導入到 /dev/null.
依邏輯來說, 指令由左到右循序執行 : stderr 導到 stdout 接著再導入 /dev/null, 不應該會漏掉 stderr 呀?
於是man 了 bash , 發現裡面可以找到詳細說明
裡面寫到, 如果要同時導入 stdout/ stderr, 應使用 > /dev/null 2>&1 的寫法.
根據裡面的說明, 可瞭解執行指令前會預先處理 input/ output. 再從左至右循序執行指令
依照這樣的邏輯就很好解釋下列情型.
-
ooxx 2>&1 > /dev/null
- stderr 會先導到 stdout, 也就是顯示在 user terminal. 然後 stdout 才會導到 /dev/null. 所以說 stderr 會被輸出!
-
ooxx > /dev/null 2>&1
- 先把 stdout 導入 /dev/null, 然後再將 stderr 導入 stdout. 因為同個 fd, 此時 stdout 已經指向 /dev/null. 所以 stderr 就會導入 /dev/null
我們再根據這樣的邏輯, 來改寫程式. 如下:
#!/bin/sh ooxx() { echo "Hello stdout" echo "Hello stderr" 1>&2 } ooxx > /dev/null 2>&1
這樣的寫法, 無論在 bash or dash 的執行環境, 都能夠成功將 stdout/ stderr 都導入 /dev/null. 如下圖所示:
那~除此之外, 還有沒有其他方法呢?
我們來 man dash, 看一下 redirect 相關章節. 其中有一行提到如何關閉 output
[n]>&- Close standard output (or n).
根據這行的提示, 我們再來改寫一下程式,
#!/bin/sh ooxx() { echo "Hello stdout" echo "Hello stderr" 1>&2 } ooxx 2>&- > /dev/null
執行結果如下:
的確都將 stdout/ stderr 的輸出關閉
因為 ooxx 2>&- > /dev/null 執行順序為
1. 先關閉 stderr 的輸出,
2. 將 stdout 導入 /dev/null
因此不會顯示任何輸出.
但如果我們想要將 stderr & stdout 導入特定檔案, 而不是 /dev/null, 就不能使用此方法了
總言之, 如果想要在 Debian/ Ubuntu 環境下進行redirect stdout/ stderr 到 /dev/null, 有三種方法
- 直接指定 bash 執行 script, 不使用預設的dash, 即可使用 “&> /dev/null“
- 使用 “> /dev/null 2>&1“
- 使用 “2>&- > /dev/null“. (此方法只適用於將結果導入 /dev/null)
ref:
[1]: http://mywiki.wooledge.org/BashFAQ/055
[2]: bash manpage
[3]: dash manpage