At a glance
tail -f app.logprints the last 10 lines and streams new entries until you press Ctrl+C.tqdml run -- ./job.shstreams everything a job prints to a Runpeek link you can follow from any browser or phone.tail -F app.logtracks the file name across log rotation and waits if the file is missing.tail -F app.log | grep --line-buffered ERRORfilters live output without stalling in pipes.journalctl -f -u nginxfollows a systemd service;docker logs -f webfollows a container.
Follow a file with tail -f
tail -f prints the last 10 lines of a file, then stays open and prints new lines as your job appends them, until you press Ctrl+C. Ten lines isn't much context if an error trace just scrolled past, so pass -n 100 to print the last 100 lines first, or -n +1 to print the whole file from line 1 and then follow.
You can also pass two or more files when a job splits stdout and stderr into separate logs. tail prints a ==> name <== header each time the output switches from one file to the other.
tail -f app.log
tail -n 100 -f app.log
tail -f stdout.log stderr.log$ tail -f stdout.log stderr.log
==> stdout.log <==
out 1
out 2
==> stderr.log <==
err 1
err 2Follow a log from your phone or another machine
tail -f only shows a log on the machine that has it, so checking a long job from your phone means an SSH app and a session that stays open. Start the job under tqdml run and Runpeek streams its stdout and stderr to a live link instead, with its tqdm bars, run time and exit code.
For a quick look from a terminal on a remote VM or cluster node, pass the tail -F command to ssh in quotes so the remote machine runs it and streams the output back. Add -t so that Ctrl+C also stops the remote tail.
ssh -t user@server 'tail -F /var/log/app.log'tail -f vs tail -F when logs rotate
Plain -f means --follow=descriptor. That works until a rotation tool renames app.log to app.log.1 and creates a fresh app.log. tail -f keeps reading the end of the renamed file, so your terminal goes quiet even though the job is still logging.
The GNU manual says descriptor tracking isn't what you want for rotated logs and points you to --follow=name, or the shorter -F, which combines two options:
--follow=namewatches the file name, so after rotationtailopens the newapp.logand tells you on stderr.--retrykeeps trying to open a file that is missing or unreadable, which helps when you start watching before the job creates its log.
$ tail -F app.log
line 1
line 2
line 3
tail: 'app.log' has been replaced; following new file
line 4
line 5When tail polls instead of using inotify, it checks about once per second (-s) and reopens a file by name after --max-unchanged-stats checks without a size change, 5 by default. Expect a few seconds between the last old line and the first new one.
Here is how the common ways to follow a log compare:
| Command | What it follows | Stop it with | Use it for |
|---|---|---|---|
tqdml run -- ./job.sh (Runpeek) | The job's stdout and stderr, on a live link | Close the page (the job keeps running) | Following a job from your phone |
tail -f app.log | The open file, even after it is renamed | Ctrl+C or --pid | Files that are never rotated |
tail -F app.log | The file name, reopened when replaced | Ctrl+C or --pid | Rotated application logs |
less +F app.log | The open file (--follow-name reopens by name) | Ctrl+C, then q | Pausing to scroll and search |
journalctl -f -u nginx | The systemd journal for one unit | Ctrl+C | systemd services |
docker logs -f web | A container's stdout and stderr | Ctrl+C (the container keeps running) | Containers |
Get-Content app.log -Wait | The file, checked once per second | Ctrl+C | Windows PowerShell |
Filter lines and stop automatically
To watch only failures, pipe tail -F into grep. In a terminal that prints matches right away, because GNU grep line-buffers output when it writes to an interactive device.
The trap shows up when you pipe grep into another command or a file. Then grep fully buffers and holds matches until its buffer fills: in a test, a file fed by grep ERROR was still empty after 4 seconds of matching lines. Add --line-buffered whenever grep's output isn't your terminal.
--pid=PID makes tail exit as soon as the process that writes the log dies. Pair it with $!, the PID of the last background job:
tail -F app.log | grep ERROR
tail -F app.log | grep --line-buffered ERROR >> errors.log
python -u train.py > train.log 2>&1 &
tail --pid=$! -F train.logless +F and journalctl -f
Sometimes you spot a stack trace and want to pause the stream to scroll up and search. less +F app.log opens the file in follow mode, where the F command keeps reading at the end of the file like tail -f. Press Ctrl+C to stop following, scroll or search with /, and press F to jump back to the live end.
If your script runs as a systemd service, its output goes to the journal instead of a file. Use journalctl -f -u <unit> to follow one unit, with -n 50 to see the last 50 entries first. For containers, see how to follow Docker logs.
less +F app.log
less --follow-name +F app.log
journalctl -f -u nginx -n 50FAQ
How do I stop tail -f?
Press Ctrl+C in the terminal where tail -f is running. If you want tail to stop on its own when the job writing the log finishes, start it with --pid=PID and the writer's process ID, and tail exits as soon as that process dies.
Why does tail -f stop showing new lines after log rotation?
Plain tail -f follows the open file, so when a log rotator renames app.log to app.log.1 and creates a new app.log, tail stays attached to the old file. Use tail -F, which follows the file name and reopens the new file after rotation.
How do I show more than 10 lines before following?
Add -n with a number, for example tail -n 100 -f app.log to print the last 100 lines first. Use -n +1 to print the entire file from line 1 and then keep following new lines.
Why does grep after tail -f print nothing when I pipe it into another command?
GNU grep only line-buffers its output when it writes to a terminal. When you pipe it into another command or redirect it to a file, it fully buffers and waits for the buffer to fill before writing anything. Add --line-buffered so it writes each matching line immediately.
Is there a tail -f for Windows?
In PowerShell 3.0 and later, use Get-Content app.log -Tail 10 -Wait. -Tail prints the last lines of the file, and -Wait keeps the file open and checks it once per second for new lines until you press Ctrl+C.
Can I follow a log from my phone?
Yes, if you start the job under tqdml run. Runpeek streams everything the command prints to stdout and stderr to a live link that opens in any mobile browser, with no account needed to watch, and a free link keeps the last 500 lines. It can't attach to a job that is already running or follow a file another process writes, so wrap the command when you start it.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- GNU coreutils manual: tail invocation gnu.org
- GNU grep manual: output line buffering gnu.org
- less(1) man page man7.org
- journalctl(1) man page freedesktop.org
- Microsoft Learn: Get-Content learn.microsoft.com