Skip to content

Logs and pipes ​

By the end of this lesson you can save a command's output and errors to files, chain small commands into a pipeline that answers a question, follow a log as it grows, and look up a service's log in the system journal.

You have already used > to make files and | to filter ps in the earlier lessons. Now you will see how they really work.

A sample log to work with ​

A log is a file a program writes to as it runs, one line per event, usually with a time and a level such as INFO, WARN or ERROR. Make a small one in your practice folder by pasting this whole block:

bash
cd ~/practice
cat > app.log <<'EOF'
2026-11-09 09:00:01 INFO  server started on port 8080
2026-11-09 09:00:05 INFO  GET /home 200
2026-11-09 09:00:07 WARN  slow response from database (1200 ms)
2026-11-09 09:00:09 ERROR database connection refused
2026-11-09 09:00:12 INFO  GET /login 200
2026-11-09 09:00:15 ERROR payment service timeout
2026-11-09 09:00:18 INFO  GET /home 200
2026-11-09 09:00:20 ERROR database connection refused
EOF

cat > app.log <<'EOF' means "write everything up to the line EOF into app.log". It is a handy way to create a file with several lines.

Two output streams: stdout and stderr ​

Every command has two separate ways to print:

text
                 ┌──────────┐ ── stdout (1): normal output ──▶ screen
 keyboard ─────▶ │ command  │
  stdin (0)      └──────────┘ ── stderr (2): error messages ─▶ screen
  • stdout, standard output, number 1: the results.
  • stderr, standard error, number 2: the complaints.
  • stdin, standard input, number 0: what the command reads, the keyboard unless you say otherwise.

On screen they look the same. You can see they are different when you ask ls for one file that exists and one that does not:

text
sam@laptop:~/practice$ ls notes.txt app.log
ls: cannot access 'notes.txt': No such file or directory
app.log

The first line came on stderr, the second on stdout.

Redirection: send output to a file ​

SymbolExampleWhat it does
>ls > out.txtstdout to a file. Replaces what was in it
>>echo done >> out.txtstdout to the end of a file. Keeps what was there
2>ls 2> err.txtstderr to a file
> file 2>&1ls > all.txt 2>&1Both to the same file
2> /dev/nullls nope 2> /dev/nullThrows errors away. /dev/null is a bin that swallows everything
<sort < names.txtReads stdin from a file
text
sam@laptop:~/practice$ ls notes.txt app.log > out.txt
ls: cannot access 'notes.txt': No such file or directory
sam@laptop:~/practice$ cat out.txt
app.log
sam@laptop:~/practice$ ls notes.txt app.log > all.txt 2>&1
sam@laptop:~/practice$ cat all.txt
ls: cannot access 'notes.txt': No such file or directory
app.log

The error still reached the screen in the first command, because only stdout went to the file. 2>&1 means "send stream 2 wherever stream 1 is going", so it must come after > all.txt.

WARNING

> empties the file before the command even starts. sort app.log > app.log leaves you with an empty app.log. Write to a new file, or use >> when you mean to add.

Pipes: one command's output is the next one's input ​

The | (pipe) connects the stdout of one command to the stdin of the next:

text
 grep ERROR app.log ──▶ cut -d' ' -f4- ──▶ sort ──▶ uniq -c ──▶ sort -rn
 the error lines        the message only   groups   counts      biggest
                                           repeats  each group  first

Each command does one small job. Together they answer a real question: which error happens most?

text
sam@laptop:~/practice$ grep ERROR app.log | cut -d' ' -f4- | sort | uniq -c | sort -rn
      2 database connection refused
      1 payment service timeout

Build a pipeline one step at a time: run the first command, look at the output, add | and the next command, run it again. Here are the tools you will reach for most:

CommandWhat it doesExample
grep wordKeeps lines containing wordgrep ERROR app.log
grep -v wordKeeps lines not containing itgrep -v INFO app.log
grep -i, -c, -EIgnore case; count matching lines; allow a|b patternsgrep -E 'WARN|ERROR' app.log
wc -lCounts linesgrep ERROR app.log | wc -l
sortSorts lines; -n by number, -r reversedsort -rn
uniq -cCollapses repeated neighbouring lines and counts them. Sort firstsort | uniq -c
head, tailThe first or last linestail -n 1
cut -d' ' -f4-Keeps fields 4 onwards, split on spacesThe message part of each line
awk '{print $3}'Prints the third word of each lineThe level column

A few questions answered from the sample log:

text
sam@laptop:~/practice$ grep -c ERROR app.log
3
sam@laptop:~/practice$ awk '{print $3}' app.log | sort | uniq -c
      3 ERROR
      4 INFO
      1 WARN
sam@laptop:~/practice$ grep -i database app.log | tail -n 1
2026-11-09 09:00:20 ERROR database connection refused

Follow a log as it grows: tail -f ​

Logs grow while programs run. tail -f (follow) prints the last lines and then waits, showing each new line as it is written:

bash
tail -f app.log

Open a second terminal, go to the same folder and add a line:

bash
echo "2026-11-09 09:01:00 ERROR disk full" >> app.log

The line appears in the first terminal straight away. To see only new errors, filter the stream: tail -f app.log | grep ERROR. Press Ctrl + C to stop following; the log itself is not touched.

tail -F (capital F) keeps following when a log is rotated, that is, renamed and replaced with a fresh file, which many systems do every day or week.

The system journal: journalctl ​

Services managed by systemd (from Processes and ports) write to the journal, a central log that journalctl reads:

CommandShows
journalctl -u cronThe log of one service (-u for unit)
journalctl -u cron -n 20Its last 20 lines
journalctl -fNew lines from everything, as they arrive, like tail -f
journalctl -p err -bOnly errors and worse, since the last boot
journalctl --since "1 hour ago"Everything from the last hour
text
sam@laptop:~$ journalctl -u cron -n 2
Nov 09 17:30:01 laptop CRON[755534]: pam_unix(cron:session): session closed for user root
…

Each line starts with the time, the machine name, and the program with its PID in square brackets. Older systems and some programs also write plain files under /var/log, which you read with less, grep and tail.

If journalctl shows only your own programs' messages, your user is not allowed to read the system journal. On Ubuntu, members of the adm group can; otherwise use sudo journalctl ….

Many logs at once: LogLens ​

When you have an app log, an API log and a server log, three tail -f windows get hard to follow. LogLens follows several files in one merged view, with a coloured label for each file:

bash
loglens app.log api.log
  • / filters as you type, like grep on a live stream.
  • e shows errors only, keeping each traceback with its error.
  • p pauses, and s searches everything it has read.
  • c groups similar errors and counts them, the same question as the sort | uniq -c pipeline above.

See the LogLens beginner guide, which includes a script that writes sample logs for you to practise on. LogLens reads files only: it does not read from a pipe, so tail -f app.log | loglens does not work.

Stay safe ​

  • > overwrites without asking. Double-check the file name, and never redirect into the file you are reading.
  • Logs can hold secrets. Tokens, passwords, email addresses and IP addresses end up in logs. Think before you paste a log into a chat, a ticket or a public issue, and remove what does not belong there.
  • Do not edit or delete system logs to make a problem go away. You lose the evidence you need. Read them, copy the part you need with grep … > notes.txt, and leave the original alone.
  • Watch the disk. A program logging in a loop can fill a disk. du -sh /var/log 2> /dev/null shows how much space the system logs take (the 2> /dev/null hides complaints about folders only root may read).

Check yourself ​

QuizLogs and pipes

Question 1 of 6

You run ls missing.txt > out.txt. The error still appears on screen and out.txt is empty. Why?

Score: 0

What you learned ​

  • Commands print results on stdout (1) and errors on stderr (2).
  • > replaces a file, >> adds to it, 2> catches errors, > file 2>&1 catches both.
  • | feeds one command's output into the next. grep, sort, uniq -c, wc -l, cut and awk are the usual building blocks.
  • tail -f follows a log live; journalctl -u NAME reads a service's log.
  • LogLens follows several logs at once, with filters and error groups.

Next steps ​