跳至主要內容
技術

Mac 的 /tmp 是我的練習場:重開機就清空,隨便玩都不怕

Mac 的 /tmp 是我的練習場:重開機就清空,隨便玩都不怕

我有個習慣:想試一個沒把握的指令、想 clone 別人的 repo 來翻翻、或是下載了一包不知道裝什麼的壓縮檔,第一步都是先 cd /tmp。

原因很簡單,這個資料夾重開機就會清空。在裡面怎麼亂玩都沒關係,玩完也不用收,下次開機它自己就乾淨了。

今天順手 ls -ld /tmp 看了一下,發現它其實不是一個資料夾:

終端機畫面:執行 ls -ld /tmp,輸出 lrwxr-xr-x@ 1 root wheel 11 Jun 25 10:29 /tmp -> private/tmp,代表 /tmp 是一條指向 private/tmp 的捷徑

箭頭 -> 的意思是 /tmp 只是一條捷徑(symbolic link),真正的資料夾在 /private/tmp。這篇就從這行輸出講起,順便聊聊我怎麼拿它當練習場。

拆開這行 ls

lrwxr-xr-x@ 1 root  wheel  11 Jun 25 10:29 /tmp -> private/tmp

一格一格看:

  • 開頭的 l:這是 link,不是資料夾。資料夾的話這格會是 d。
  • 權限後面的 @:代表它身上掛著 extended attributes(延伸屬性)。用 xattr -sl /tmp 可以看到是 com.apple.rootless,這是 SIP(系統完整性保護)的標記,意思是這條捷徑屬於系統檔案,連 root 都不能改。
  • root wheel:擁有者是 root,群組是 wheel。
  • 11:捷徑的大小是 11 bytes。捷徑裡只存了一個路徑字串,private/tmp 剛好 11 個字元,用 echo -n private/tmp | wc -c 算一下就知道。
  • Jun 25 10:29:跟 /bin/ls 這些系統檔案的時間戳一模一樣。這條捷徑是 macOS 本身的一部分,不是誰後來建的。

想看真正的資料夾,在路徑後面多加一個斜線,ls 就會跟著捷徑走進去:

$ ls -ld /tmp/
drwxrwxrwt  115 root  wheel  3680 Sep 23 16:16 /tmp/

這次開頭變成 d 了。權限 rwxrwxrwt 前面代表所有人都能讀、能寫、能進去,最後那個 t 叫 sticky bit:大家都能在裡面放東西,但只能刪自己的。這台 Mac 上每個帳號、每支背景程式都在共用 /tmp,就是靠這個 t 才不會互相刪到對方的檔案。

重開機真的會清空嗎?我比對了開機時間

「/tmp 重開機就清空」這句話大家都這樣講,這次想實際驗證一下。我沒有真的去重開機,而是比對兩個時間:這次開機的時間,跟 /private/tmp 這個資料夾的建立時間。

# 這次開機的時間
sysctl kern.boottime
# { sec = 1789701419, usec = 548252 } Fri Sep 18 11:16:59 2026

# /private/tmp 這個資料夾的建立時間(%SB 是 birth time)
stat -f '%SB' /private/tmp
# Sep 18 11:17:15 2026

資料夾本身的建立時間,只比開機晚 16 秒。所以它不只是「把裡面的東西刪光」,是整個資料夾在開機時重新建立一次。我把裡面 113 個項目的建立時間也全部掃過,沒有一個早於這次開機。

以前的 Mac OS X 把這件事寫得很直白。Apple 開源的開機腳本 rc.cleanup(檔頭寫著 Copyright 2002 Apple Computer)裡有這一段:

# Clean out /private/tmp.
if [ -d /private/tmp ]; then
    # (清掉上次殘留 _tmp_ 的部分略過)
    echo -n " /private/tmp"
    mv /private/tmp /private/_tmp_
    (chflags -R nouchg /private/_tmp_ && rm -rf /private/_tmp_) &
fi
mkdir -m 1777 /private/tmp

先把整個資料夾改名成 _tmp_,丟到背景慢慢刪,再立刻建一個全新的 /private/tmp。這樣開機不用等舊檔案刪完,新的 /tmp 馬上就能用。1777 就是前面看到的 rwxrwxrwt,最前面那個 1 代表 sticky bit。

現在的 macOS 改由 launchd 負責開機,這支腳本已經不在了,我也沒找到現在是哪段程式在做這件事。不過從建立時間看,結果跟當年一樣:整個資料夾重建。

對照組是 /var/tmp:

stat -f '%SB' /private/var/tmp
# Jun 25 10:29:03 2026

它三個月前就在了,9 月 18 日那次開機並沒有動到它。macOS 的 man hier 對 /var/tmp 的說明是:

temporary files that are kept between system reboots

也就是「重開機會保留的暫存檔」。名字很像,一個會清一個不會,練習的時候別放錯地方。

不重開機,放三天沒碰也會被清

那只要不關機,是不是就能一直放?也不行。macOS 有一支每天跑的清理工作,設定檔在 /System/Library/LaunchDaemons/com.apple.tmp_cleaner.plist,用 plutil -p 打開來看:

"ProgramArguments" => [
  0 => "/usr/libexec/tmp_cleaner"
]
"StartCalendarInterval" => {
  "Hour" => 0
}

每天凌晨 0 點那個時段會跑 /usr/libexec/tmp_cleaner。它是一支 shell script,可以直接 cat 出來看,關鍵是這幾行:

daily_clean_tmps_dirs="/tmp"				# Delete under here
daily_clean_tmps_days="3"				# If not accessed for

args="-atime +$daily_clean_tmps_days -mtime +$daily_clean_tmps_days"
args="${args} -ctime +$daily_clean_tmps_days"

白話翻譯:/tmp 底下的檔案,如果最後讀取、最後修改、最後變更屬性的時間全部超過 3 天,就刪掉。放了超過 3 天的空資料夾也會一起清掉。

還有一點跟 cron 不一樣:0 點的時候 Mac 如果在睡覺,launchd 會等下次喚醒再補跑。man launchd.plist 是這樣寫的:

Unlike cron which skips job invocations when the computer is asleep, launchd will start the job the next time the computer wakes up.

所以筆電蓋起來過夜也躲不掉。整理起來,一個丟進 /tmp 的檔案大概會有這幾種下場:

flowchart TB
    A["在 /tmp 建立一個檔案"] --> B{"接下來發生什麼事?"}
    B -->|"重開機"| C["開機時 /private/tmp<br/>整個重新建立<br/>檔案消失"]
    B -->|"連續 3 天沒讀、沒改"| D["tmp_cleaner 凌晨 0 點刪掉<br/>Mac 在睡覺就等喚醒後補跑"]
    B -->|"你想留下來"| E["搬到 ~/ 底下<br/>才算真的存起來"]

冷知識:這支腳本的變數名是從 FreeBSD 搬來的

tmp_cleaner 明明是一支獨立的小程式,變數卻都叫 daily_clean_tmps_ 開頭,看起來像是從哪裡搬來的。

man tmp_cleaner 的 HISTORY 只有一行:

tmp_cleaner first appeared in macOS 14.0

它是 macOS Sonoma 才出現的新程式,但內容很舊。舊版 macOS 用的是 periodic 這套每日維護機制,其中清 /tmp 的腳本叫 /etc/periodic/daily/110.clean-tmps。Apple 開源的版本檔頭寫著:

# $FreeBSD: src/etc/periodic/daily/110.clean-tmps,v 1.13 2004/02/28 04:58:40 ache Exp $

這是直接從 FreeBSD 搬來的。它的設定檔 periodic.conf 裡有這三行:

daily_clean_tmps_enable="YES"				# Delete stuff daily
daily_clean_tmps_dirs="/tmp"				# Delete under here
daily_clean_tmps_days="3"				# If not accessed for

跟現在的 tmp_cleaner 比一比,變數名一樣,連行尾註解「Delete under here」「If not accessed for」都一字不差,-atime、-mtime、-ctime 三個條件也是原封不動。daily_ 這個前綴,是它當年身為 periodic「每日任務」留下來的名字。我這台 macOS 26 已經沒有 /etc/periodic 資料夾了,但這三天的規則還活在 tmp_cleaner 裡。

我拿 /tmp 來做什麼

知道它會自己清乾淨之後,/tmp 就變成很放心的練習場。幾個我覺得特別適合的用法:

練習有破壞力的指令。 rm、mv、find -delete 這類指令,萬用字元會刪到哪些檔案,先在 /tmp 做一個假資料夾試過再說:

mkdir -p /tmp/play && cd /tmp/play
touch a.txt b.txt c.log
rm *.txt   # 確認只刪到 .txt,再拿去真正的資料夾用

clone 別人的 repo 來翻翻。 只是想看看某個專案怎麼寫的,不打算長期留著,就丟到 /tmp。加 --depth 1 只抓最新版本,比較快:

cd /tmp && git clone --depth 1 https://github.com/withastro/astro.git

看專案產生器會生出什麼。 npm create 這類 scaffolding 工具會一口氣生出一堆檔案。想先看看它生了什麼,就在 /tmp 跑一次,不會弄髒自己的專案資料夾。

看壓縮檔裡到底裝什麼。 從網路上抓的壓縮檔,先解到 /tmp 裡看一眼:

mkdir /tmp/peek && tar -xzf ~/Downloads/something.tar.gz -C /tmp/peek

懶得想資料夾名稱。 用 mktemp -d 每次都會拿到一個不重複的新資料夾:

cd "$(mktemp -d)"

要注意的是,macOS 上的 mktemp -d 不是建在 /tmp,而是建在 $TMPDIR,路徑長得像 /var/folders/86/…/T/tmp.y7ks3XlAK6。這是每個使用者自己的暫存區,下一節會講到它。

用之前要知道的三件事

/tmp 是全機共用的,別放祕密。 macOS 預設的 umask 是 022,意思是你在 /tmp 建的檔案,其他帳號讀得到。密碼、token、.env 這類東西別丟進來。需要私人的暫存空間就用 $TMPDIR:

$ ls -ld "$TMPDIR"
drwx------@ 2085 bobochen  staff  66720 Sep 23 16:21 /var/folders/86/…/T/

權限是 rwx------,只有你自己進得去。

如果是自己寫程式要用暫存檔,也別把 /tmp 寫死。Apple 自己的 File System Programming Guide 就這樣講:

The primary directory (/tmp) is where most local files go, but you should never hardcode this path into your application.

用語言內建的函式去拿暫存資料夾就好,像 Node.js 的 os.tmpdir()、Python 的 tempfile.gettempdir(),在 macOS 上拿到的都是 $TMPDIR。

路徑有時候會顯示成 /private/tmp。 在 /tmp 裡用 pwd 會顯示 /tmp,但 pwd -P(顯示實際路徑)會顯示 /private/tmp。有些工具的錯誤訊息或輸出會印出 /private/tmp/...,那是同一個地方,不用緊張。

cd /tmp
pwd      # /tmp
pwd -P   # /private/tmp

/tmp 是練習場,不是沙盒。 重開機會清空,不代表在裡面跑東西比較安全。在 /tmp 執行的程式,權限跟在家目錄執行一模一樣,該讀得到的檔案它都讀得到。解壓縮來看看沒問題,來路不明的 script 就算放在 /tmp 也別執行。

冷知識:/tmp 其實橫跨了兩顆磁碟

macOS Catalina 開始,系統被切成兩個 volume:一個是唯讀的系統磁碟,一個是放你的資料的「Macintosh HD - Data」。Apple 的說明文件是這樣寫的:

macOS Catalina or later runs on a dedicated, read-only system volume called Macintosh HD. This volume is completely separate from all other data to help prevent the accidental overwriting of critical operating system files.

在終端機打 mount 也看得到,根目錄 / 被標成 sealed 跟 read-only:

/dev/disk3s3s1 on / (apfs, sealed, local, read-only, journaled)
/dev/disk3s1 on /System/Volumes/Data (apfs, local, journaled, nobrowse, protect, root data)

系統磁碟既然是唯讀的,/tmp 這種要讓大家隨時寫入的地方怎麼辦?答案藏在 /usr/share/firmlinks 這個檔案裡。打開來會看到一行:

/private	private

firmlink 是 Apple 為了這種雙磁碟架構做的特殊連結,讓系統磁碟上的 /private 直接通到資料磁碟上的 private。所以一個寫進 /tmp 的檔案,實際上走了兩段路:

flowchart TB
    A["/tmp<br/>系統磁碟上的捷徑<br/>唯讀、SIP 保護"] -->|"symlink"| B["/private/tmp"]
    B -->|"firmlink"| C["資料磁碟上的 private/tmp<br/>真正可以寫入的地方"]

這也解釋了開頭那行 ls 的兩個細節:/tmp 身上的 com.apple.rootless 標記,跟它和 /bin/ls 一模一樣的時間戳,因為它就住在那顆唯讀的系統磁碟上,跟著 macOS 一起安裝、一起更新。

一句話總結

/tmp 是 Mac 送你的免洗練習場:重開機整個重建、三天沒碰半夜收走、全機帳號共用。拿它練手、翻別人的程式碼、看壓縮檔都很適合,只要記得一件事:想留下來的東西,記得搬回家目錄。

參考資料

留言討論

esc
輸入關鍵字搜尋文章...
查看收藏 →