- Linux
Linux statコマンドの使い方|atime・mtime・ctime早見表
statコマンドとは、ファイルのサイズ・権限・所有者・inode番号・4種類のタイムスタンプ(atime・mtime・ctime・Birth)をまとめて表示するコマンドです。 ls -l が見せるのは主に更新日時(mtime)1つですが、stat なら「最後に読まれた時刻」「権限が変わった時刻」「作られた時刻」までナノ秒単位で確認できます。
この記事では、cat・追記・chmod・chown・mv・cp・touch・ln などの操作ごとにどのタイムスタンプが変わるかを実際に測った一覧表、stat -c の書式指定子の全一覧とコピペ用ワンライナー、relatime で atime が動かない理由、macOS(BSD)版 stat との対応、file・ls -l との使い分けをまとめます。実行結果はすべて Ubuntu 24.04 / GNU coreutils 9.4 / ext4(relatime)/ カーネル 6.18 で root ユーザーとして動かしたものです。macOS 側は今回動かしていないため「未実行」と明記しています。
statとタイムスタンプの早見表
4つのタイムスタンプ
| 表示名 | 呼び名 | 意味 | 書式指定子(読みやすい形/エポック秒) | ls での表示 |
|---|---|---|---|---|
| Access | atime | 中身を最後に読んだ時刻 | %x / %X | ls -lu |
| Modify | mtime | 中身を最後に書き換えた時刻 | %y / %Y | ls -l(既定) |
| Change | ctime | inode の情報(権限・所有者・名前・リンク数・サイズ)が最後に変わった時刻 | %z / %Z | ls -lc |
| Birth | btime(crtime) | ファイルが作られた時刻 | %w / %W | ls -l --time=birth |
ctime は「作成(create)」ではなく「変更(change)」の c です。 作成時刻は Birth です。
操作ごとに変わるタイムスタンプ(実測)
「変」は時刻が更新されたもの、「―」は変わらなかったものです。
| 操作 | atime | mtime | ctime | Birth | 補足 |
|---|---|---|---|---|---|
| cat f.txt(書き込み直後の1回目) | 変 | ― | ― | ― | relatime の条件に当てはまるため更新 |
| cat f.txt(2回目) | ― | ― | ― | ― | relatime では毎回は更新しない |
| ls -l f.txt、stat f.txt | ― | ― | ― | ― | 情報を見るだけでは何も変わらない |
| echo two >> f.txt(追記) | ― | 変 | 変 | ― | 中身が変わるとサイズも変わるため ctime も動く |
| echo new > f.txt(上書き) | ― | 変 | 変 | ― | 同じ inode のまま中身を入れ替える |
| truncate -s 0 f.txt | ― | 変 | 変 | ― | |
| chmod 600 f.txt | ― | ― | 変 | ― | 権限は inode の情報 |
| chown nobody f.txt | ― | ― | 変 | ― | 所有者も inode の情報 |
| mv f.txt g.txt(同じファイルシステム内) | ― | ― | 変 | ― | 改名でも中身は触らない |
| ln f.txt hard.txt(ハードリンク作成) | ― | ― | 変 | ― | リンク数が増える |
| rm hard.txt(ハードリンクを1つ削除) | ― | ― | 変 | ― | リンク数が減る |
| ln -s f.txt sym.txt(リンク先の f.txt 側) | ― | ― | ― | ― | シンボリックリンクは別の inode |
| touch f.txt | 変 | 変 | 変 | ― | |
| touch -a f.txt | 変 | ― | 変 | ― | atime だけ指定しても ctime は動く |
| touch -m f.txt | ― | 変 | 変 | ― | |
| touch -d "2020-01-01 00:00" f.txt | 変 | 変 | 変 | ― | 過去の日付にできるのは atime と mtime。ctime は現在時刻になる |
| cp f.txt c.txt(既存のコピー先 c.txt) | ― | 変 | 変 | ― | コピー先は上書きされる |
| cp -p f.txt c.txt(既存のコピー先 c.txt) | 変 | 変 | 変 | ― | atime・mtime がコピー元と同じ値になる |
| sed -i で置き換え保存 | 変 | 変 | 変 | 変 | 新しいファイルを作って入れ替えるため inode 番号も変わる |
| gzip してから gunzip | ― | ― | 変 | 変 | 作り直されるが、mtime は元の値が復元される |
覚え方は「中身を書いたら mtime と ctime、属性だけなら ctime、読んだら atime(ただし条件つき)」です。 ctime は touch でも過去の値に設定できないため、「最近なにか手が入ったか」を確かめる手がかりになります。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linux statコマンドの使い方|atime・mtime・ctime早見表」
statの基本的な使い方と出力の読み方
stat memo.txt
実行結果:
File: memo.txt
Size: 6 Blocks: 8 IO Block: 4096 regular file
Device: 254,0 Inode: 3211666 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2026-10-03 14:27:45.998729750 +0900
Modify: 2026-10-03 14:27:46.003304640 +0900
Change: 2026-10-03 14:27:46.005260727 +0900
Birth: 2026-10-03 14:27:45.998729750 +0900
| 項目 | この例の値 | 意味 |
|---|---|---|
| File | memo.txt | ファイル名。シンボリックリンクなら「名前 -> リンク先」 |
| Size | 6 | バイト数 |
| Blocks | 8 | 割り当てられたブロック数(1ブロック 512 バイト単位。%B で確認できる) |
| IO Block | 4096 | 入出力に適したブロックサイズ |
| 種類 | regular file | 通常ファイル。ほかに directory、symbolic link、fifo、socket、character special file など |
| Device | 254,0 | ファイルがあるデバイスの番号(メジャー,マイナー) |
| Inode | 3211666 | inode 番号。同じファイルシステム内で同じ番号なら同じ実体 |
| Links | 1 | ハードリンクの数 |
| Access(1つ目) | 0644/-rw-r--r-- | 権限。8進表記と記号表記 |
| Uid / Gid | 0/root | 所有者とグループ(番号/名前) |
| Access(2つ目)/Modify/Change/Birth | 時刻 | atime/mtime/ctime/作成時刻 |
「Access」が2回出てきますが、1つ目は権限(アクセス権)、2つ目は最終アクセス時刻です。
よく使うオプション
| オプション | 働き | 例 |
|---|---|---|
| (なし) | 上のような詳細表示 | stat memo.txt |
| -c 書式、--format=書式 | 必要な項目だけを表示(末尾に改行が付く) | stat -c '%a %n' memo.txt |
| --printf=書式 | -c と同じだが改行を付けない。\n や \t が使える | stat --printf '%n\t%s\n' memo.txt |
| -L、--dereference | シンボリックリンクをたどり、リンク先の情報を表示 | stat -L link.txt |
| -f、--file-system | ファイルではなく、そのファイルが載っているファイルシステムの情報 | stat -f . |
| -t、--terse | 1行にまとめた機械処理向けの表示 | stat -t memo.txt |
シンボリックリンクに対しては、既定ではリンクそのものの情報が出ます。
$ stat link.txt
File: link.txt -> memo.txt
Size: 8 Blocks: 0 IO Block: 4096 symbolic link
Device: 254,0 Inode: 3211668 Links: 1
$ stat -L link.txt
File: link.txt
Size: 6 Blocks: 8 IO Block: 4096 regular file
Device: 254,0 Inode: 3211666 Links: 1
(それぞれ先頭3行を抜粋)-L を付けると Size が 8(リンク先の名前 memo.txt の文字数)から 6(中身のバイト数)に変わり、Inode も memo.txt と同じ番号になります。
stat -f でファイルシステムの情報を見る
$ stat -f .
File: "."
ID: 55d9f9279eba0be0 Namelen: 255 Type: ext2/ext3
Block size: 4096 Fundamental block size: 4096
Blocks: Total: 66053021 Free: 56375732 Available: 1285236
Inodes: Total: 16777216 Free: 15993961
$ stat -f -c '%T ブロック=%S 空き=%a/%b' .
ext2/ext3 ブロック=4096 空き=1285236/66053021
ext4 でも Type は「ext2/ext3」と表示されます(3つは同じ識別番号を共有しているためです)。ファイル名の最大長(Namelen)や inode の残り数をスクリプトで取りたいときに便利です。
存在しないファイルを指定したとき
$ stat nothing.txt
stat: cannot statx 'nothing.txt': No such file or directory
$ echo $?
1
終了コードが 1 になるので、スクリプトでは存在確認を兼ねて使えます。
stat -c の書式指定子 全一覧
GNU coreutils 9.4 の stat --help に載っている指定子を、memo.txt での実際の出力と合わせて整理しました。
ファイル用
| 指定子 | 意味 | memo.txt での出力 |
|---|---|---|
| %n | ファイル名 | memo.txt |
| %N | 引用符つきのファイル名。リンクならリンク先も表示 | 'memo.txt'(link.txt では 'link.txt' -> 'memo.txt') |
| %s | サイズ(バイト) | 6 |
| %b | 割り当てブロック数 | 8 |
| %B | %b の1ブロックあたりのバイト数 | 512 |
| %o | 入出力に適した転送サイズ | 4096 |
| %a | 権限(8進) | 644 |
| %A | 権限と種類(記号) | -rw-r--r-- |
| %f | モードの生の値(16進) | 81a4 |
| %F | ファイルの種類 | regular file |
| %u | 所有者のユーザーID | 0 |
| %U | 所有者のユーザー名 | root |
| %g | グループID | 0 |
| %G | グループ名 | root |
| %i | inode 番号 | 3211666 |
| %h | ハードリンク数 | 1 |
| %m | マウントポイント | / |
| %d | デバイス番号(10進) | 65024 |
| %D | デバイス番号(16進) | fe00 |
| %Hd | デバイスのメジャー番号 | 254 |
| %Ld | デバイスのマイナー番号 | 0 |
| %r | デバイス種別(10進、デバイスファイル用) | 0 |
| %R | デバイス種別(16進、デバイスファイル用) | 0 |
| %Hr | デバイス種別のメジャー番号(10進) | 0 |
| %Lr | デバイス種別のマイナー番号(10進) | 0 |
| %t | デバイス種別のメジャー番号(16進) | 0 |
| %T | デバイス種別のマイナー番号(16進) | 0 |
| %C | SELinux のセキュリティコンテキスト | ?(SELinux 無効の環境のため取得できず) |
| %x | atime(読みやすい形) | 2026-10-03 14:27:45.998729750 +0900 |
| %X | atime(エポック秒) | 1791005265 |
| %y | mtime(読みやすい形) | 2026-10-03 14:27:46.003304640 +0900 |
| %Y | mtime(エポック秒) | 1791005266 |
| %z | ctime(読みやすい形) | 2026-10-03 14:27:46.005260727 +0900 |
| %Z | ctime(エポック秒) | 1791005266 |
| %w | 作成時刻(読みやすい形)。不明なら - | 2026-10-03 14:27:45.998729750 +0900 |
| %W | 作成時刻(エポック秒)。不明なら 0 | 1791005265 |
時刻の指定子は「小文字が読みやすい形、大文字がエポック秒」と覚えると迷いません(x/X、y/Y、z/Z、w/W)。
ファイルシステム用(-f と組み合わせる)
| 指定子 | 意味 |
|---|---|
| %n | ファイル名 |
| %T | ファイルシステムの種類(読みやすい形) |
| %t | ファイルシステムの種類(16進) |
| %i | ファイルシステムID(16進) |
| %l | ファイル名の最大長 |
| %s | ブロックサイズ(転送向け) |
| %S | 基本ブロックサイズ(ブロック数の計算用) |
| %b | 全データブロック数 |
| %f | 空きブロック数 |
| %a | 一般ユーザーが使える空きブロック数 |
| %c | inode の総数 |
| %d | 空き inode 数 |
同じ文字でも、-f の有無で意味が変わります(%a はファイル用では権限、ファイルシステム用では空きブロック数)。
コピペ用ワンライナー集
すべて実行して出力を確認しています。
| やりたいこと | コマンド | 出力例 |
|---|---|---|
| 更新日時だけ取る | stat -c '%y' memo.txt | 2026-10-03 14:27:46.003304640 +0900 |
| 更新日時を秒まで(小数点以下を落とす) | stat -c '%y' memo.txt | cut -d. -f1 | 2026-10-03 14:27:46 |
| 更新日時をエポック秒で取る | stat -c '%Y' memo.txt | 1791005266 |
| 更新日時を好きな形式で | date -r memo.txt '+%Y%m%d_%H%M%S' | 20261003_142746 |
| エポック秒を日時に戻す | date -d "@$(stat -c %Y memo.txt)" '+%Y-%m-%d %H:%M:%S' | 2026-10-03 14:27:46 |
| 権限を8進で取る | stat -c '%a' memo.txt | 644 |
| 権限を8進とファイル名で並べる | stat -c '%a %n' memo.txt | 644 memo.txt |
| 権限を記号で取る | stat -c '%A' memo.txt | -rw-r--r-- |
| サイズ(バイト)だけ取る | stat -c '%s' memo.txt | 6 |
| 所有者:グループ | stat -c '%U:%G' memo.txt | root:root |
| inode 番号 | stat -c '%i' memo.txt | 3211666 |
| ハードリンク数 | stat -c '%h' memo.txt | 1 |
| ファイルの種類 | stat -c '%F' memo.txt | regular file |
| リンク先を表示 | stat -c '%N' link.txt | 'link.txt' -> 'memo.txt' |
| 作成時刻 | stat -c '%w' memo.txt | 2026-10-03 14:27:45.998729750 +0900 |
| タブ区切りで名前とサイズ | stat --printf '%n\t%s\n' memo.txt run.sh | memo.txt(タブ)6、run.sh(タブ)20 の2行 |
| ファイルシステムの種類 | stat -f -c '%T' . | ext2/ext3 |
スクリプトでの使用例
# 権限が 755 かどうか確かめる
[ "$(stat -c %a run.sh)" = 755 ] && echo "run.sh is 755"
# 最後の更新から何秒たったか
echo $(( $(date +%s) - $(stat -c %Y memo.txt) )) sec
# カレントディレクトリのファイルを「権限 所有者 サイズ 名前」で一覧
stat -c '%a %U %s %n' *
# 更新が新しい順に下から3件(エポック秒で並べ替え)
stat -c '%Y %n' * | sort -n | tail -3
実行結果(1つ目、2つ目、3つ目の一部):
run.sh is 755
0 sec
644 root 8 conf.json
644 root 256 data.bin
644 root 0 empty
777 root 8 link.txt
755 root 4096 logs
644 root 6 memo.txt
755 root 20 run.sh
シンボリックリンク(link.txt)の権限は 777 と表示されます。リンク自体の権限は使われず、実際にはリンク先の権限で判定されます。
atime・mtime・ctime・Birth の違いを確かめる
早見表の結果は、次のスクリプトで「操作の前後で stat の値を比べる」方法で測りました。手元の環境で確かめたいときにそのまま使えます。
#!/bin/bash
snap(){ stat -c '%x|%y|%z|%w|%i' "$1"; }
cmp(){ local label="$1" b="$2" a="$3" r=""
IFS='|' read -r ba bm bc bb bi <<<"$b"; IFS='|' read -r aa am ac ab ai <<<"$a"
[ "$ba" != "$aa" ] && r="$r atime"; [ "$bm" != "$am" ] && r="$r mtime"
[ "$bc" != "$ac" ] && r="$r ctime"; [ "$bb" != "$ab" ] && r="$r Birth"
[ "$bi" != "$ai" ] && r="$r (inode変化)"
printf '%-40s | %s\n' "$label" "${r:- なし}"; }
# op ラベル 操作前に見るファイル 操作後に見るファイル コマンド
op(){ local b a; b=$(snap "$2"); sleep 0.05; eval "$4" >/dev/null 2>&1; sleep 0.02; a=$(snap "$3"); cmp "$1" "$b" "$a"; }
echo one > f.txt; sleep 0.05
op "cat f.txt (1回目)" f.txt f.txt 'cat f.txt'
op "cat f.txt (2回目)" f.txt f.txt 'cat f.txt'
op "echo two >> f.txt" f.txt f.txt 'echo two >> f.txt'
op "chmod 600 f.txt" f.txt f.txt 'chmod 600 f.txt'
op "mv f.txt g.txt" f.txt g.txt 'mv f.txt g.txt'
実行結果:
cat f.txt (1回目) | atime
cat f.txt (2回目) | なし
echo two >> f.txt | mtime ctime
chmod 600 f.txt | ctime
mv f.txt g.txt | ctime
cp と mv で日時がどうなるか
コピーでは、新しくできたファイルの mtime は「コピーした時刻」になります。元の日時を引き継ぎたいときは cp -p を使います。
touch -d "2020-01-01 00:00" f.txt
cp f.txt n.txt
cp -p f.txt p.txt
stat -c '%n mtime=%y birth=%w' f.txt n.txt p.txt
f.txt mtime=2020-01-01 00:00:00.000000000 +0900 birth=2026-10-03 14:27:24.690728484 +0900
n.txt mtime=2026-10-03 14:27:24.791428691 +0900 birth=2026-10-03 14:27:24.786728489 +0900
p.txt mtime=2020-01-01 00:00:00.000000000 +0900 birth=2026-10-03 14:27:24.791428691 +0900
| 操作 | できたファイルの mtime | できたファイルの Birth |
|---|---|---|
| cp f.txt n.txt | コピーした時刻 | コピーした時刻 |
| cp -p f.txt p.txt | コピー元と同じ | コピーした時刻 |
| mv f.txt g.txt(同じファイルシステム内) | 変わらない | 変わらない(inode がそのまま) |
mtime は引き継げても、Birth は引き継げません。 Birth はそのファイルシステム上で inode が作られた時刻だからです。別のファイルシステムへの mv は「コピーして元を消す」動きになるため、Birth は新しくなります(この点は今回は測っていません)。
ディレクトリのタイムスタンプ
ディレクトリの mtime は「中の名前の一覧」が変わったときに動きます。
| 操作 | ディレクトリ d の atime | mtime | ctime |
|---|---|---|---|
| d の中にファイルを新規作成(touch d/a) | ― | 変 | 変 |
| d の中のファイルに追記(echo x >> d/a) | ― | ― | ― |
| d の中で改名(mv d/a d/b) | ― | 変 | 変 |
| d の中のファイルを削除(rm d/b) | ― | 変 | 変 |
| ls d(1回目) | 変 | ― | ― |
| ls d(2回目) | ― | ― | ― |
中のファイルを編集しても、ディレクトリの mtime は変わりません。 「フォルダの更新日時は以前のままなのに、中のファイルは新しい」という状態は正常です。
relatime・noatimeでatimeが動かない理由
「cat したのに atime が変わらない」のは、マウントオプション relatime の働きです。読むたびに atime を書き込むとディスクへの書き込みが増えるため、Linux では relatime が既定になっています。
relatime では、次のどちらかに当てはまるときだけ atime を更新します。
| 条件 | 今回の実測 |
|---|---|
| 現在の atime が mtime または ctime より前(=最後に読んだあとで変更があった) | 書き込み直後の1回目の cat で atime が更新された |
| 現在の atime が24時間より前 | atime を2日前にしてから cat すると更新された |
| 上のどちらでもない | 2回目の cat では変わらなかった |
24時間の条件は次のように確かめました。
echo x > r.txt; cat r.txt > /dev/null
touch -a -d "2 days ago" r.txt # atime だけを2日前にする
cat r.txt > /dev/null # → atime が更新された
cat r.txt > /dev/null # → 変わらなかった
マウントオプションごとの違いは、小さな tmpfs を3種類のオプションでマウントして確かめました(同じファイルを続けて読み、3回目の cat の前後を比較)。
mkdir m_noatime
mount -t tmpfs -o noatime,size=1m tmpfs m_noatime
| マウントオプション | 3回目の cat で atime が変わったか | 動き |
|---|---|---|
| relatime(既定) | 変わらない | 条件に当てはまるときだけ更新 |
| noatime | 変わらない | 読んでも更新しない |
| strictatime | 変わった | 読むたびに更新 |
いま使っているファイルシステムのオプションは、次のコマンドで確認できます。
$ findmnt -T . -o TARGET,FSTYPE,OPTIONS
TARGET FSTYPE OPTIONS
/ ext4 rw,relatime,discard,resv_strict,resuid=65534,resgid=65534
| 目的 | 見るタイムスタンプ | 注意 |
|---|---|---|
| 最後に中身が変わった時刻 | mtime | cp -p や touch -d で任意の値にできる |
| 最後に何らかの変更があった時刻 | ctime | 利用者が任意の値に設定する手段が無い |
| 最近読まれたかどうかの目安 | atime | relatime では「1日単位の目安」と考える。noatime では使えない |
| 作られた時刻 | Birth | ファイルシステムの対応が必要 |
Birth(作成時刻)が「-」になる場合
Birth は、ファイルシステムが作成時刻を記録していて、かつ stat がそれを取得できる場合に表示されます。今回の環境では ext4 と tmpfs で表示され、/proc 配下のファイルでは「-」でした。
$ stat -c '%w' /dev/shm /proc/version
2026-10-03 14:16:48.598690672 +0900
-
「-」と表示される場合は、そのファイルシステムが作成時刻を持っていないか、以前のバージョンの環境で取得に対応していないかのどちらかです。作成時刻が必要な処理では、%W が 0 でないことを確かめてから使うと安心です。
inode番号で同じ実体かを確かめる、日時でファイルを探す
ハードリンクとコピーを見分ける
inode 番号(%i)とリンク数(%h)を並べると、同じ実体かどうかが一目でわかります。 a.txt にハードリンク b.txt、コピー c.txt、シンボリックリンク s.txt を作って比べました。
echo data > a.txt
ln a.txt b.txt # ハードリンク
cp a.txt c.txt # コピー
ln -s a.txt s.txt # シンボリックリンク
stat -c '%i %h %n' a.txt b.txt c.txt s.txt
1393093 2 a.txt
1393093 2 b.txt
1394104 1 c.txt
1394116 1 s.txt
| ファイル | inode 番号 | リンク数 | 読み取れること |
|---|---|---|---|
| a.txt と b.txt | 同じ | 2 | 名前が2つある同じ実体。片方を書き換えるともう片方も変わる |
| c.txt | 別 | 1 | 中身が同じだけの別のファイル |
| s.txt | 別 | 1 | a.txt という名前を指す小さな別ファイル |
シェルの条件式では -ef で同じ判定ができます。
[ a.txt -ef b.txt ] && echo "a.txt と b.txt は同じ実体"
[ a.txt -ef c.txt ] || echo "a.txt と c.txt は別の実体"
a.txt と b.txt は同じ実体
a.txt と c.txt は別の実体
タイムスタンプを条件にファイルを探す
stat は「1つのファイルの日時を見る」コマンドです。「日時を条件に探す」ときは find を組み合わせます。3日前の日時にした old.log と、作ったばかりの new.log で確かめました。
| やりたいこと | コマンド | 実行結果 |
|---|---|---|
| 24時間以内に更新されたファイル | find . -mtime -1 -name '*.log' | ./new.log |
| 更新から2日以上たったファイル | find . -mtime +1 -name '*.log' | ./old.log |
| 基準のファイルより後に更新されたファイル | find . -newer old.log -type f | ./new.log など4件 |
| 更新日つきで一覧 | find . -name '*.log' -printf '%TY-%Tm-%Td %p\n' | 日付とパスの2行 |
-mtime を -atime や -ctime に替えると、それぞれのタイムスタンプで探せます。atime を条件にする場合は、前の章のとおり relatime の環境では1日単位の目安になる点を考慮します。
GNU statとmacOS(BSD)statの対応表
macOS の stat は BSD 系で、オプションの意味が GNU 版と異なります。特に -f は、GNU では「ファイルシステム情報」、BSD では「書式指定」と、まったく別の働きです。
以下の macOS 側は今回の環境では未実行で、一般に知られている BSD stat の仕様に基づく対応です。利用前に手元の man stat で確かめてください。
| やりたいこと | Linux(GNU stat・実行して確認) | macOS(BSD stat・未実行) |
|---|---|---|
| 書式を指定する | stat -c '書式' ファイル | stat -f '書式' ファイル |
| 更新日時(エポック秒) | stat -c '%Y' | stat -f '%m' |
| 更新日時(読みやすい形) | stat -c '%y' | stat -f '%Sm' |
| 日時の表示形式を指定 | date -r ファイル '+%Y-%m-%d' | stat -f '%Sm' -t '%Y-%m-%d' |
| アクセス日時(エポック秒) | stat -c '%X' | stat -f '%a' |
| 状態変更日時(エポック秒) | stat -c '%Z' | stat -f '%c' |
| 作成日時(エポック秒) | stat -c '%W' | stat -f '%B' |
| 権限(8進) | stat -c '%a' | stat -f '%Lp' |
| 権限(記号) | stat -c '%A' | stat -f '%Sp' |
| サイズ(バイト) | stat -c '%s' | stat -f '%z' |
| 所有者名 | stat -c '%U' | stat -f '%Su' |
| グループ名 | stat -c '%G' | stat -f '%Sg' |
| inode 番号 | stat -c '%i' | stat -f '%i' |
| ハードリンク数 | stat -c '%h' | stat -f '%l' |
| ファイル名 | stat -c '%n' | stat -f '%N' |
| リンクをたどる | stat -L | stat -L |
| Linux 風の詳細表示 | stat(既定) | stat -x |
| ファイルシステム情報 | stat -f | (stat には無い。df などを使う) |
両方で動かすスクリプトでは、次のように切り替える方法があります(Linux 側の分岐のみ実行して確認)。
mtime_epoch() {
if stat -c '%Y' "$1" >/dev/null 2>&1; then
stat -c '%Y' "$1" # GNU
else
stat -f '%m' "$1" # BSD
fi
}
mtime_epoch memo.txt
ファイルの種類を確認する:stat・file・ls -l の使い分け
「このファイルは何か」を知りたいとき、3つのコマンドは見ているものが違います。
| コマンド | 見ているもの | わかること | 向いている場面 |
|---|---|---|---|
| ls -l | inode の種類(先頭1文字) | 通常ファイル・ディレクトリ・リンクなどの区別 | 一覧でざっと確認 |
| stat -c '%F' | inode の種類(言葉で) | 同上を文字列で | スクリプトで種類を判定 |
| file | ファイルの中身 | テキストか、画像か、圧縮ファイルか、実行ファイルか | 拡張子が無い・信用できないファイルの確認 |
ls -l の先頭1文字と stat %F の対応
$ ls -l
-rw-r--r-- 1 root root 6 Oct 3 14:27 memo.txt
drwxr-xr-x 2 root root 4096 Oct 3 14:27 logs
lrwxrwxrwx 1 root root 8 Oct 3 14:27 link.txt -> memo.txt
prw-r--r-- 1 root root 0 Oct 3 14:27 pipe
-rwxr-xr-x 1 root root 20 Oct 3 14:27 run.sh
$ ls -ld /dev/null
crw-rw-rw- 1 root root 1, 3 Oct 3 06:55 /dev/null
(ls -l の出力は該当行を抜粋)
| ls -l の先頭 | stat -c '%F' の出力 | 種類 | 今回の確認 |
|---|---|---|---|
| - | regular file | 通常ファイル | 実行して確認 |
| d | directory | ディレクトリ | 実行して確認 |
| l | symbolic link | シンボリックリンク | 実行して確認 |
| p | fifo | 名前付きパイプ | 実行して確認 |
| c | character special file | キャラクタデバイス | 実行して確認(/dev/null) |
| s | socket | ソケット | 実行して確認(%F のみ) |
| b | block special file | ブロックデバイス | 未実行(この環境に該当ファイルが無い) |
file コマンドで中身から判定する
file は拡張子ではなく、中身の先頭部分などから種類を推定します(file 5.45 で実行)。
$ file *
conf.json: JSON text data
empty: empty
items.csv: CSV ASCII text
items.csv.gz: gzip compressed data, was "items.csv", last modified: Sat Oct 3 05:27:46 2026, from Unix, original size modulo 2^32 14
link.txt: symbolic link to memo.txt
logs: directory
memo.txt: ASCII text
photo.dat: PNG image data, 16 x 16, 8-bit/color RGB, non-interlaced
pipe: fifo (named pipe)
run.sh: Bourne-Again shell script, ASCII text executable
photo.dat は拡張子が .dat ですが、中身から PNG 画像と判定されています。
| やりたいこと | コマンド | 出力 |
|---|---|---|
| 種類だけ表示(ファイル名を省く) | file -b memo.txt | ASCII text |
| MIME タイプと文字コード | file -i memo.txt | memo.txt: text/plain; charset=us-ascii |
| MIME タイプだけ | file --mime-type -b photo.dat | image/png |
| リンク先の中身を判定 | file -L link.txt | link.txt: ASCII text |
file の判定は推定です。規則性のないバイナリデータ(0〜255 を順に並べた 256 バイトのファイル)を渡したところ「Targa image data ...」と表示されました。結果は手がかりとして使い、重要な判定では中身を別の方法でも確かめるのが安全です。
よくある質問
Q. ctime を過去の日時に変更できますか? A. touch では変更できません。今回の測定でも、touch -d で atime と mtime を 2020 年にしたとき、ctime は実行した時刻になりました。ctime はカーネルが inode を更新したときに自動で記録する値です。
Q. ls -l の日時と stat のどれが同じですか? A. ls -l が既定で表示するのは mtime(Modify)です。ls -lu で atime、ls -lc で ctime、ls -l --time=birth で作成時刻を表示できます。秒より細かい値まで見たいときは stat を使います。
Q. ファイルを編集していないのに ctime だけ新しいのはなぜですか? A. 権限の変更(chmod)、所有者の変更(chown)、改名や移動(mv)、ハードリンクの作成や削除で ctime だけが動きます。早見表のとおり、これらの操作では mtime は変わりません。
Q. エディタで保存したら inode 番号が変わりました。 A. 「新しいファイルに書いてから元の名前に入れ替える」方式で保存するツールでは、inode が新しくなります。今回の sed -i でも inode 番号と Birth が変わりました。ハードリンクを張っているファイルでは、リンクが別々の実体になる点に気をつけます。
まとめ
- stat は4つのタイムスタンプ(atime・mtime・ctime・Birth)と権限・所有者・inode をまとめて表示する
- 中身を書いたら mtime と ctime、属性だけなら ctime、読んだら atime(条件つき)が動く
- ctime は change(変更)であって作成ではない。作成時刻は Birth(%w)
- 必要な項目だけ取るなら stat -c。更新日時は %y/%Y、8進の権限は %a、サイズは %s
- atime が動かないのは relatime の働き。atime が mtime・ctime より前のとき、または24時間より前のときだけ更新される
- macOS の stat は -f が書式指定。GNU の -c に相当し、指定子も異なる(未実行・要確認)
- 種類の確認は、inode の種類なら ls -l と stat %F、中身の種類なら file
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linux statコマンドの使い方|atime・mtime・ctime早見表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。