Shell exec命令操作檔案描述符

2020-07-16 10:04:45
exec 是 Shell 內建命令,它有兩種用法,一種是執行 Shell 命令,一種是操作檔案描述符。本節只講解後面一種,前面一種請大家自行學習。

使用 exec 命令可以永久性地重定向,後續命令的輸入輸出方向也被確定了,直到再次遇到 exec 命令才會改變重定向的方向;換句話說,一次重定向,永久有效。

嗯?什麼意思?難道說我們以前使用的重定向都是臨時的嗎?是的!前面使用的重定向都是臨時的,它們只對當前的命令有效,對後面的命令無效。

請看下面的例子:
[[email protected] ~]$ echo "c.biancheng.net" > log.txt
[[email protected] ~]$ echo "C語言中文網"
C語言中文網
[[email protected] ~]$ cat log.txt
c.biancheng.net
第一個 echo 命令使用了重定向,將內容輸出到 log.txt 檔案;第二個 echo 命令沒有再次使用重定向,內容就直接輸出到顯示器上了。很明顯,重定向只對第一個 echo 有效,對第二個 echo 無效。

有些指令碼檔案的輸出內容很多,我們不希望直接輸出到顯示器上,或者我們需要把輸出內容備份到檔案中,方便以後檢索,按照以前的思路,必須在每個命令後面都使用一次重定向,寫起來非常麻煩。如果以後想修改重定向的方向,那工作量也是不小的。

exec 命令就是為解決這種困境而生的,它可以讓重定向對當前 Shell 進程中的所有命令有效,它的用法為:

exec 檔案描述符操作

在《結合Linux檔案描述符談重定向,徹底理解重定向的本質》一節講到的所有對檔案描述符的操作方式 exec 都支援,請看下面的例子:
[[email protected] ~]$ echo "重定向未發生"
重定向未發生
[[email protected] ~]$ exec >log.txt
[[email protected] ~]$ echo "c.biancheng.net"
[[email protected] ~]$ echo "C語言中文網"
[[email protected] ~]$ exec >&2
[[email protected] ~]$ echo "重定向已恢復"
重定向已恢復
[[email protected] ~]$ cat log.txt
c.biancheng.net
C語言中文網
對程式碼的說明:
  • exec >log.txt將當前 Shell 進程的所有標準輸出重定向到 log.txt 檔案,它等價於exec 1>log.txt
  • 後面的兩個 echo 命令都沒有在顯示器上輸出,而是輸出到了 log.txt 檔案。
  • exec >&2用來恢復重定向,讓標準輸出重新回到顯示器,它等價於exec 1>&2。2 是標準錯誤輸出的檔案描述符,它也是輸出到顯示器,並且沒有遭到破壞,我們用 2 來覆蓋 1,就能修復 1,讓 1 重新指向顯示器。
  • 接下來的 echo 命令將結果輸出到顯示器上,證明exec >&2奏效了。
  • 最後我們用 cat 命令來檢視 log.txt 檔案的內容,發現就是中間兩個 echo 命令的輸出。

重定向的恢復

類似echo "1234" >log.txt這樣的重定向只是臨時的,當前命名執行完畢後會自動恢復到顯示器,我們不用擔心。但是諸如exec >log.txt這種使用 exec 命令的重定向都是持久的,如果我們想再次回到顯示器,就必須手動恢復。

以輸出重定向為例,手動恢復的方法有兩種:
  • /dev/tty 檔案代表的就是顯示器,將標準輸出重定向到 /dev/tty 即可,也就是 exec >/dev/tty。
  • 如果還有別的檔案描述符指向了顯示器,那麼也可以別的檔案描述符來恢復標號為 1 的檔案描述符,例如 exec >&2。注意,如果檔案描述符 2 也被重定向了,那麼這種方式就無效了。

下面的例子演示了輸入重定向的恢復:
#!/bin/bash

exec 6<&0  #先將0號檔案描述符儲存
exec <nums.txt  #輸入重定向

sum=0
while read n; do
    ((sum += n))
done
echo "sum=$sum"

exec 0<&6 6<&-  #恢復輸入重定向,並關閉檔案描述符6

read -p "請輸入名字、網址和年齡:" name url age
echo "$name已經$age歲了,它的網址是 $url"
將程式碼儲存到 test.txt,並執行下面的命令:
[[email protected] ~]$ cat nums.txt
80
33
129
71
100
222
8
[[email protected] ~]$ bash ./test.sh
sum=643
請輸入名字、網址和年齡:C語言中文網 http://c.biancheng.net 7
C語言中文網已經7歲了,它的網址是 http://c.biancheng.net