📊 stat Command

Display Detailed File and Filesystem Information

About the stat Command

The stat command displays detailed information about files and filesystems. Unlike ls which shows basic file listings, stat reveals comprehensive metadata including inode numbers, permissions in multiple formats, timestamps (access, modification, change), file size in bytes and blocks, device information, and more. It's an essential diagnostic tool for system administrators, forensics, and troubleshooting file-related issues.

Key capabilities: Display file metadata, show inode information, reveal all three timestamps (atime, mtime, ctime), format output for scripting, query filesystem information, follow or not follow symbolic links, and provide machine-readable output.

Basic Syntax: stat [OPTIONS] FILE... Common Options: -L, --dereference Follow symbolic links -f, --file-system Display filesystem information instead of file info -c, --format=FORMAT Use custom format instead of default -t, --terse Print information in terse (machine-readable) form --printf=FORMAT Like --format but interpret backslash escapes Common Format Sequences: %n File name %s Total size in bytes %b Number of blocks allocated %F File type (regular file, directory, symbolic link, etc.) %a Access rights in octal %A Access rights in human-readable form %u User ID of owner %U User name of owner %g Group ID %G Group name %x Time of last access %y Time of last modification %z Time of last status change %i Inode number

Detailed Examples

1Basic File Information

Display comprehensive information about a file.

$ stat example.txt
File: example.txt Size: 1024 Blocks: 8 IO Block: 4096 regular file Device: 802h/2050d Inode: 12345678 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 1000/ craig) Gid: ( 1000/ craig) Access: 2025-11-28 10:00:00.123456789 -0500 Modify: 2025-11-28 09:55:00.987654321 -0500 Change: 2025-11-28 09:55:00.987654321 -0500 Birth: 2025-11-28 09:50:00.000000000 -0500
What's happening: Stat displays complete file metadata. Line 1 - File name: The target file. Line 2 - Size/Blocks: Size is 1024 bytes, using 8 blocks (blocks are typically 512 bytes). IO Block shows filesystem block size. Line 3 - Device/Inode: Device ID (802h = hexadecimal, 2050d = decimal), unique inode number, and hard link count. Line 4 - Permissions: Shows both octal (0644) and symbolic (-rw-r--r--) permissions, plus owner UID/name and group GID/name. Lines 5-8 - Timestamps: Access (last read), Modify (last content change), Change (last metadata change), Birth (creation - not all filesystems support this). Why this matters: These details are crucial for debugging permission issues, understanding file history, forensic analysis, and verifying file integrity.
The three timestamps tell you: Access = when read, Modify = when content changed, Change = when metadata (permissions, ownership) changed. Change time always updates when Modify updates.

2Custom Format Output

Extract specific information using format strings.

$ stat -c "%n: %s bytes" example.txt
example.txt: 1024 bytes
$ stat -c "File: %n Owner: %U (UID: %u) Permissions: %a (%A) Size: %s bytes" example.txt
File: example.txt Owner: craig (UID: 1000) Permissions: 644 (-rw-r--r--) Size: 1024 bytes
$ stat -c "%n,%s,%y" *.txt
file1.txt,2048,2025-11-28 10:00:00.000000000 -0500 file2.txt,4096,2025-11-28 10:05:00.000000000 -0500 file3.txt,1024,2025-11-28 10:10:00.000000000 -0500
What's happening: The -c flag allows custom format strings. First example: Shows just filename and size in a concise format. Second example: Multi-line custom output with labels. Newlines in the format string are preserved. Third example: CSV-style output for multiple files - perfect for importing into spreadsheets or databases. Format sequences like %n (name), %s (size), %y (modify time) extract specific fields. Scripting power: Custom formats make stat output parseable by scripts. Instead of parsing human-readable output, you can extract exact fields needed. This is essential for automation, monitoring, and data collection scripts.
Common scripting pattern: stat -c "%n,%s,%Y" * where %Y gives Unix timestamp (seconds since epoch) instead of human-readable time - perfect for date calculations.

3Terse Output for Scripting

Machine-readable format for automated processing.

$ stat -t example.txt
example.txt 1024 8 81a4 1000 1000 802 12345678 1 0 0 1732806000 1732805700 1732805700 1732805400 4096
$ stat -t example.txt | awk '{print "File: "$1" Size: "$2" Owner: "$5}'
File: example.txt Size: 1024 Owner: 1000
What's happening: The -t (terse) flag outputs all information in a single line with space-separated fields - ideal for parsing with awk, cut, or other text processing tools. Field order: name, size, blocks, mode (hex), uid, gid, device, inode, links, major, minor, atime, mtime, ctime, birth, blocksize. Second example: Shows parsing terse output with awk. Fields are positional: $1=name, $2=size, $5=uid, etc. Why terse mode: Consistent field positions make parsing reliable. Human-readable format varies and is hard to parse. Terse format is stable and designed for scripts. Use case: Collecting file statistics across many files for analysis, generating reports, or monitoring file changes over time.
Terse output fields are in a specific order. Always consult documentation or test to verify field positions for your stat version.

4Comparing File Timestamps

Understanding and working with the three timestamps.

$ touch test.txt $ stat -c "Access: %x Modify: %y Change: %z" test.txt
Access: 2025-11-28 11:00:00.000000000 -0500 Modify: 2025-11-28 11:00:00.000000000 -0500 Change: 2025-11-28 11:00:00.000000000 -0500
$ echo "new content" >> test.txt $ stat -c "Access: %x Modify: %y Change: %z" test.txt
Access: 2025-11-28 11:00:00.000000000 -0500 Modify: 2025-11-28 11:01:00.000000000 -0500 Change: 2025-11-28 11:01:00.000000000 -0500
$ chmod 755 test.txt $ stat -c "Access: %x Modify: %y Change: %z" test.txt
Access: 2025-11-28 11:00:00.000000000 -0500 Modify: 2025-11-28 11:01:00.000000000 -0500 Change: 2025-11-28 11:02:00.000000000 -0500
What's happening: Demonstrates the three timestamps and what triggers their updates. First: Fresh file via touch - all three timestamps are identical. Second: Appending content updates Modify and Change (but not Access in this example). Third: Changing permissions (metadata) updates only Change time, not Modify or Access. Understanding timestamps: Access (atime): Last read. Note: Many systems mount with relatime/noatime for performance, so atime may not update on every read. Modify (mtime): Last content modification. This is what ls -l shows. Change (ctime): Last metadata change - permissions, ownership, hard links, or content. Cannot be set manually (unlike atime/mtime). Forensic importance: Timestamp analysis helps determine file history, detect tampering, investigate incidents, and understand system activity.
Find recently modified files: find . -type f -mtime -1 (last 24 hours) or check specific times with stat to verify backups, deployments, or changes.

5Filesystem Information

Query filesystem details instead of file details.

$ stat -f /
File: "/" ID: a2b3c4d5e6f7a8b9 Namelen: 255 Type: ext4 Block size: 4096 Fundamental block size: 4096 Blocks: Total: 25600000 Free: 15360000 Available: 14336000 Inodes: Total: 6400000 Free: 5120000
$ stat -fc "%n: %T filesystem, %a/%b blocks free" /home /var
/home: ext4 filesystem, 14336000/25600000 blocks free /var: ext4 filesystem, 8192000/12800000 blocks free
What's happening: The -f flag shows filesystem information instead of file information. Output details: ID is the filesystem ID, Namelen is maximum filename length, Type shows filesystem type (ext4, xfs, btrfs, etc.), Block size information, Total/Free/Available blocks (Available accounts for reserved blocks), Total/Free inodes. Second example: Custom format for filesystem stats. %n = path, %T = type, %a = available blocks, %b = total blocks. Use cases: Monitoring disk space at filesystem level, checking filesystem type before operations, verifying mount points, capacity planning, and troubleshooting space issues. Available vs Free: "Free" is total unused space, "Available" is what non-root users can use (some space is reserved for root).
Check filesystem fullness: stat -fc "$(echo ~)/: %a/%b = $((100*%a/%b))% free" ~ calculates percentage free space.

6Symbolic Links

Examining symbolic links vs. their targets.

$ ln -s /etc/passwd link_to_passwd $ stat link_to_passwd
File: link_to_passwd -> /etc/passwd Size: 11 Blocks: 0 IO Block: 4096 symbolic link Device: 802h/2050d Inode: 98765432 Links: 1 Access: (0777/lrwxrwxrwx) Uid: ( 1000/ craig) Gid: ( 1000/ craig)
$ stat -L link_to_passwd
File: link_to_passwd Size: 2048 Blocks: 8 IO Block: 4096 regular file Device: 802h/2050d Inode: 11111111 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
What's happening: Stat handles symbolic links specially. Without -L: Shows information about the link itself - size is the length of the target path string, type is "symbolic link", permissions are always 777 (symbolic links have no meaningful permissions). With -L (dereference): Follows the link and shows information about the target file (/etc/passwd). Now you see the actual file's size (2048 bytes), type (regular file), real permissions (644), and owner (root). Why this matters: Links can mask true file properties. Checking the link itself tells you about the link; checking with -L tells you about the target. For broken links (target doesn't exist), -L will fail with an error. Use case: Verifying link targets, debugging broken links, understanding file relationships, and checking actual vs. apparent permissions.
Symbolic link permissions (always 777) don't matter - the target file's permissions control access. Stat without -L is useful to verify the link exists and points where expected.

7Scripting: Find Large Files

Using stat in scripts to analyze file characteristics.

#!/bin/bash # Find files larger than 100MB threshold=$((100 * 1024 * 1024)) # 100 MB in bytes find /home/user -type f -print0 | while IFS= read -r -d '' file; do size=$(stat -c %s "$file") if [ "$size" -gt "$threshold" ]; then human_size=$(stat -c %s "$file" | numfmt --to=iec) echo "$human_size $file" fi done | sort -rh
500M /home/user/videos/movie.mp4 250M /home/user/backup.tar.gz 150M /home/user/large_dataset.csv
What's happening: This script finds files exceeding 100MB. Step 1: find locates all files (null-separated for safety with special characters). Step 2: stat -c %s extracts exact size in bytes. Step 3: Compare against threshold. Step 4: For large files, convert size to human-readable format with numfmt and print. Step 5: Sort by size (largest first) using sort -rh. Why stat instead of ls: Stat gives precise byte counts and is more scriptable. stat -c %s is cleaner than parsing ls -l output. Practical uses: Disk space analysis, finding bloat, identifying files to archive, capacity planning, and quota management.
Find files by age: find . -type f | while read f; do echo "$(stat -c %Y "$f") $f"; done | sort -rn sorts by modification timestamp.

8Scripting: Verify Backup Integrity

Compare files using stat to verify backups.

#!/bin/bash # Compare source and backup files source_file="$1" backup_file="$2" if [ ! -f "$source_file" ] || [ ! -f "$backup_file" ]; then echo "Error: Files not found" exit 1 fi source_size=$(stat -c %s "$source_file") backup_size=$(stat -c %s "$backup_file") source_mtime=$(stat -c %Y "$source_file") backup_mtime=$(stat -c %Y "$backup_file") echo "Source: $source_file" echo " Size: $source_size bytes" echo " Modified: $(date -d @$source_mtime)" echo "" echo "Backup: $backup_file" echo " Size: $backup_size bytes" echo " Modified: $(date -d @$backup_mtime)" echo "" if [ "$source_size" -eq "$backup_size" ]; then echo "✓ Sizes match" else echo "✗ Size mismatch!" exit 1 fi if [ "$source_mtime" -eq "$backup_mtime" ]; then echo "✓ Modification times match" else echo "⚠ Modification times differ" fi
$ ./verify_backup.sh document.txt backup/document.txt Source: document.txt Size: 4096 bytes Modified: Thu Nov 28 11:00:00 EST 2025 Backup: backup/document.txt Size: 4096 bytes Modified: Thu Nov 28 11:00:00 EST 2025 ✓ Sizes match ✓ Modification times match
What's happening: This script verifies backup integrity by comparing file metadata. Comparisons: Size comparison catches incomplete backups or corruption. Modification time comparison detects if backup is outdated. Why not just size: Size alone isn't sufficient - a corrupted file might have the same size. However, mismatched size definitively indicates a problem. Enhancement possibilities: Add checksum comparison (md5sum/sha256sum) for content verification, check permissions with stat -c %a, verify ownership, or check inode numbers for hard links. Real-world use: Automated backup verification, disaster recovery prep, compliance checking, and ensuring backups are current before critical operations.

9Monitoring File Changes

Track when files were last modified for monitoring.

#!/bin/bash # Monitor config files for changes config_files=( "/etc/nginx/nginx.conf" "/etc/ssh/sshd_config" "/etc/mysql/my.cnf" ) log_file="/var/log/config_monitor.log" for file in "${config_files[@]}"; do if [ -f "$file" ]; then mtime=$(stat -c %Y "$file") mtime_human=$(stat -c %y "$file") echo "$(date '+%Y-%m-%d %H:%M:%S') - $file - Modified: $mtime_human" >> "$log_file" fi done
#!/bin/bash # Alert if file modified in last hour critical_file="/etc/passwd" current_time=$(date +%s) file_mtime=$(stat -c %Y "$critical_file") age=$((current_time - file_mtime)) if [ "$age" -lt 3600 ]; then echo "ALERT: $critical_file was modified $age seconds ago!" fi
What's happening: First script: Monitors important config files by logging their modification times. %Y gives Unix timestamp (seconds since epoch) for easy calculations. %y gives human-readable format for logging. Run this via cron to track config changes over time. Second script: Checks if a critical file (/etc/passwd) was modified recently. Calculates age by subtracting file mtime from current time. If modified in last hour (3600 seconds), triggers alert. Use cases: Security monitoring (unauthorized changes to system files), compliance auditing (who changed what when), change management (tracking configuration updates), and troubleshooting (correlating file changes with system issues).
Combine with inotify-tools for real-time monitoring: inotifywait -m /etc/nginx/nginx.conf watches for changes immediately rather than polling.

10Advanced: Inode and Hard Link Analysis

Understanding inodes and finding hard links.

$ echo "content" > original.txt $ ln original.txt hardlink.txt $ stat -c "%n: inode=%i links=%h" original.txt hardlink.txt
original.txt: inode=12345678 links=2 hardlink.txt: inode=12345678 links=2
#!/bin/bash # Find all hard links to a file target_file="$1" inode=$(stat -c %i "$target_file") device=$(stat -c %d "$target_file") echo "Finding all hard links to $target_file (inode: $inode)..." find / -xdev -inum "$inode" 2>/dev/null
$ ./find_hardlinks.sh original.txt Finding all hard links to original.txt (inode: 12345678)... /home/user/original.txt /home/user/hardlink.txt /backup/original.txt
What's happening: Hard links: Multiple directory entries pointing to the same inode (same physical data). First example: Both files have identical inode numbers (12345678) and link count (2), proving they're hard links to the same data. Modifying either file affects both since they share data. Second script: Finds all hard links by searching for matching inode numbers. %i = inode, %d = device (important: inodes are unique per device, not globally). find -inum locates all files with that inode. -xdev prevents crossing filesystems. Why this matters: Hard links save space (one copy of data, multiple names), but can be confusing. Deleting one hard link doesn't delete the data until all links are removed. Stat reveals the link count, helping understand file relationships. Use cases: Space analysis, understanding backup structures, file deduplication verification, and troubleshooting "file won't delete" issues.
Hard links can only exist on the same filesystem. Symbolic links can cross filesystems. Hard links can't reference directories (except . and ..). Symbolic links can.

Format Sequences Reference

Format Description Example Output
%n File name example.txt
%N Quoted file name with dereferencing if symlink 'link' -> 'target'
%s Total size in bytes 1024
%b Number of blocks allocated 8
%F File type regular file
%a Access rights in octal 644
%A Access rights in human-readable form -rw-r--r--
%u User ID of owner 1000
%U User name of owner craig
%g Group ID 1000
%G Group name users
%x Time of last access 2025-11-28 10:00:00.000000000 -0500
%X Time of last access (Unix timestamp) 1732806000
%y Time of last modification 2025-11-28 09:55:00.000000000 -0500
%Y Time of last modification (Unix timestamp) 1732805700
%z Time of last status change 2025-11-28 09:55:00.000000000 -0500
%Z Time of last status change (Unix timestamp) 1732805700
%i Inode number 12345678
%h Number of hard links 1

Common Use Cases

Quick Size Check

stat -c "%n: %s bytes" file.txt

Permission Verification

stat -c "Permissions: %a (%A)" /etc/passwd

Find Recently Modified Files

find . -type f -exec stat -c "%Y %n" {} \; | sort -rn | head -10

Check if File Changed Today

[ $(stat -c %Y file.txt) -gt $(date -d "today 00:00" +%s) ] && echo "Modified today"

Related Commands

  • ls: List directory contents with basic file info
  • file: Determine file type by examining contents
  • find: Search for files and can execute stat on results
  • df: Report filesystem disk space usage
  • du: Estimate file and directory space usage
  • lsattr/chattr: List/change file attributes on Linux
  • touch: Change file timestamps
  • chmod/chown: Change permissions/ownership (which stat displays)