Bash 指令碼如何建立臨時檔案:mktemp 命令和 trap 命令教程

阮一峰發表於2019-12-29

有時,Bash 指令碼需要建立臨時檔案或臨時目錄。

常見的做法是,自己在/tmp目錄裡面生成一個檔案,這樣做有很多弊端。本文介紹如何安全地處理臨時檔案。

一、臨時檔案的安全問題

直接建立臨時檔案,尤其在/tmp目錄裡面,往往會導致安全問題。

首先,/tmp目錄是所有人可讀寫的,任何使用者都可以往該目錄裡面寫檔案。建立的臨時檔案也是所有人可讀的。


$ touch /tmp/info.txt
$ ls -l /tmp/info.txt
-rw-r--r-- 1 ruanyf ruanyf 0 12月 28 17:12 /tmp/info.txt

上面命令在/tmp目錄直接建立檔案,該檔案預設是所有人可讀的。

其次,如果攻擊者知道臨時檔案的檔名,他可以建立符號連結,連結到臨時檔案,可能導致系統執行異常。攻擊者也可能向指令碼提供一些惡意資料。因此,臨時檔案最好使用不可預測、每次都不一樣的檔名。

最後,臨時檔案使用完畢,應該刪除。但是,指令碼意外退出時,往往會忽略清理臨時檔案。

二、臨時檔案的最佳實踐

指令碼生成臨時檔案,應該遵循下面的規則。

  • 建立前檢查檔案是否已經存在。
  • 確保臨時檔案已成功建立。
  • 臨時檔案必須有許可權的限制。
  • 臨時檔案要使用不可預測的檔名。
  • 指令碼退出時,要刪除臨時檔案(使用trap命令)。

三、mktemp 命令的用法

mktemp命令就是為安全建立臨時檔案而設計的。雖然在建立臨時檔案之前,它不會檢查臨時檔案是否存在,但是它支援唯一檔名和清除機制,因此可以減輕安全攻擊的風險。

直接執行mktemp命令,就能生成一個臨時檔案。


$ mktemp
/tmp/tmp.4GcsWSG4vj

$ ls -l /tmp/tmp.4GcsWSG4vj
-rw------- 1 ruanyf ruanyf 0 12月 28 12:49 /tmp/tmp.4GcsWSG4vj

上面命令中,mktemp命令生成的臨時檔名是隨機的,而且許可權是隻有使用者本人可讀寫。

Bash 指令碼使用mktemp命令的用法如下。


#!/bin/bash

TMPFILE=$(mktemp)
echo "Our temp file is $TMPFILE"

為了確保臨時檔案建立成功,mktemp命令後面最好使用 OR 運算子(||),指定建立失敗時退出指令碼。


#!/bin/bash

TMPFILE=$(mktemp) || exit 1
echo "Our temp file is $TMPFILE"

為了保證指令碼退出時臨時檔案被刪除,可以使用trap命令指定退出時的清除操作(詳見後文)。


#!/bin/bash

trap 'rm -f "$TMPFILE"' EXIT

TMPFILE=$(mktemp) || exit 1
echo "Our temp file is $TMPFILE"

四、mktemp 命令的引數

-d引數可以建立一個臨時目錄。


$ mktemp -d
/tmp/tmp.Wcau5UjmN6

-p引數可以指定臨時檔案所在的目錄。預設是使用$TMPDIR環境變數指定的目錄,如果這個變數沒設定,那麼使用/tmp目錄。


$ mktemp -p /home/ruanyf/
/home/ruanyf/tmp.FOKEtvs2H3

-t引數可以指定臨時檔案的檔名模板,模板的末尾必須至少包含三個連續的X字元,表示隨機字元,建議至少使用六個X。預設的檔名模板是tmp.後接十個隨機字元。


$ mktemp -t mytemp.XXXXXXX
/tmp/mytemp.yZ1HgZV

五、trap 命令的用法

trap命令用來在 Bash 指令碼中響應系統訊號。

最常見的系統訊號就是 SIGINT(中斷),即按 Ctrl + C 所產生的訊號。trap命令的-l引數,可以列出所有的系統訊號。


$ trap -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT
 4) SIGILL   5) SIGTRAP  6) SIGABRT
 ... ...

trap的命令格式如下。


$ trap [動作] [訊號]

上面程式碼中,"動作"是一個 Bash 命令,"訊號"常用的有以下幾個。

  • HUP:編號1,指令碼與所在的終端脫離聯絡。
  • INT:編號2,使用者按下 Ctrl + C,意圖讓指令碼中止執行。
  • QUIT:編號3,使用者按下 Ctrl + 斜槓,意圖退出指令碼。
  • KILL:編號9,該訊號用於殺死程式。
  • TERM:編號15,這是kill命令發出的預設訊號。
  • EXIT:編號0,這不是系統訊號,而是 Bash 指令碼特有的訊號,不管什麼情況,只要退出指令碼就會產生。

trap命令響應EXIT訊號的寫法如下。


$ trap 'rm -f "$TMPFILE"' EXIT

上面命令中,指令碼遇到EXIT訊號時,就會執行rm -f "$TMPFILE"

trap 命令的常見使用場景,就是在 Bash 指令碼中指定退出時執行的清理命令。


#!/bin/bash

trap 'rm -f "$TMPFILE"' EXIT

TMPFILE=$(mktemp) || exit 1
ls /etc > $TMPFILE
if grep -qi "kernel" $TMPFILE; then
  echo 'find'
fi

上面程式碼中,不管是指令碼正常執行結束,還是使用者按 Ctrl + C 終止,都會產生EXIT訊號,從而觸發刪除臨時檔案。

注意,trap命令必須放在指令碼的開頭。否則,它上方的任何命令導致指令碼退出,都不會被它捕獲。

如果trap需要觸發多條命令,可以封裝一個 Bash 函式。


function egress {
  command1
  command2
  command3
}

trap egress EXIT

六、參考連結

(完)

相關文章