Skip to content
shellmap

Watch a file for changes

React when a file is modified — for tail-style log viewing, hot-reload tooling, or "wait until this completes" automation.

How to watch a file for changes in each shell

Bashunix
tail -F file.log

For log tailing, `tail -F` (capital F) is the right choice — it follows the file BY NAME, so logrotate rotations (`logrotate` deletes the old file and creates a new one with the same name) are handled transparently. `tail -f` (lowercase) follows by inode and silently stops working after rotation. For arbitrary "react to ANY change" needs, `inotifywait -m file` (Linux, `apt install inotify-tools`) emits an event per write/modify. `watch -d -n 1 stat file.log` polls metadata every second and highlights changes.

Zshunix
tail -F file.log

Same external. macOS has no native `inotifywait` — use `fswatch` (`brew install fswatch`) instead: `fswatch -o file | xargs -n1 -I{} echo "changed"`. fswatch uses macOS's native `FSEvents` API (efficient, event-driven, no polling). For Linux+macOS portability, fswatch works on both.

Fishunix
tail -F file.log

Same external. Fish syntax for "run a command on change": `fswatch -o file | while read; <command-here>; end`. The `read` consumes one event per change-batch (fswatch coalesces rapid changes into one event with a debounce window — `-l 0.5` to tune).

PowerShellwindows
Get-Content file.log -Wait -Tail 20

The pwsh equivalent of `tail -F`. `-Wait` keeps the cmdlet alive, polling for new lines; `-Tail 20` starts by showing the last 20 lines. For event-driven monitoring beyond just appended lines: `$fsw = New-Object IO.FileSystemWatcher 'C:\path'; $fsw.Filter = 'file.log'; $fsw.EnableRaisingEvents = $true; Register-ObjectEvent $fsw 'Changed' -Action { Write-Host "changed at $(Get-Date)" }`. The `FileSystemWatcher` runs on a background runspace; unregister with `Unregister-Event` to stop.

cmd.exewindows
powershell -NoProfile -Command "Get-Content file.log -Wait -Tail 20"

cmd has NO native file-watch. Even the legacy `find /v "" file.log` doesn't follow appends. Shell out to pwsh (above). Or install BusyBox for Windows (`busybox64 tail -f file.log`). For repeated polling: `:loop & type file.log & timeout /t 2 & goto loop` (refreshes every 2s; inefficient because it re-reads the whole file each iteration).

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • Polling vs event-driven matters at scale. `tail -F` and `Get-Content -Wait` POLL (re-stat the file every ~100ms-1s). `inotifywait`, `fswatch`, and `FileSystemWatcher` are EVENT-DRIVEN (kernel notifies on change). For one or two files, polling is fine. For watching a directory tree of thousands of files (build tools, dev servers), event-driven is mandatory — polling everything would burn CPU constantly.
  • `FileSystemWatcher` on Windows is famous for "missed events" under high write churn. The internal buffer is 8KB by default — if many files change faster than the consumer processes them, events are dropped silently. Bump with `$fsw.InternalBufferSize = 64KB`. For mission-critical watch (CI hot-reload, deployment triggers), pair with a "list-files-modified-after-last-tick" sanity sweep.
  • On WSL2, `inotifywait` works for files INSIDE the WSL filesystem (`/home/user/...`). It does NOT work for `/mnt/c/...` (Windows-side files mounted into WSL) — the DrvFs adapter doesn't emit inotify events. For watching Windows-side files from WSL, fall back to polling, or use `pwsh.exe -Command "Get-Content -Wait ..."` from inside WSL.
  • `tail -F` is universally available; `inotifywait` requires `inotify-tools` (Linux only). For cross-platform watch in shell scripts, `entr` is the portable pick — `brew install entr` (macOS), `apt install entr` (Linux). Usage: `ls *.py | entr -r python app.py` watches the listed files and re-runs the command on any change. The `-r` restarts the previous instance on each change (perfect for dev servers).

Related commands

Related tasks