Save everything a terminal session prints

tee for one command, script for the whole session

Updated 2026-09-06

Problem: You want a record of what happened in a terminal, either because somebody asked you to send it or because you are about to break something and want to know what you did.

Solution:

For one command, tee writes the output to a file and to the screen at once:

wc -l data.txt | tee count.log
cat count.log
2 data.txt
2 data.txt

For a whole session, script records everything until you type exit:

script session.log

How it works:

tee sits in a pipeline and copies its input to a file and to standard output, so you still see what you would have seen. Everything downstream of it still works, so it belongs in the middle of a pipeline rather than at the end.

script is different in kind. It starts a new shell under a pseudo-terminal and records the whole conversation: what you typed, what came back, and anything a program wrote straight to the terminal instead of through a pipe. tee never sees that third category, which is why an installer or a progress bar shows up in a typescript and vanishes from a pipeline.

The file script writes opens and closes with a line of its own:

Script started on 2026-09-06 10:20:07+00:00 [COMMAND="..." TERM="xterm"]

Those lines are the record of when you started, not part of your session. script -q suppresses the messages on screen and keeps them in the file.

Variations:

tee only receives standard output, so an error message goes past it to the terminal and never reaches the file. Redirect stderr into the pipe first:

ls data.txt missing.txt 2>&1 | tee out.log >/dev/null
cat out.log
ls: cannot access 'missing.txt': No such file or directory
data.txt

The trailing >/dev/null throws away tee's copy of the output. Use it when you want the file and not the screen.

tee -a appends instead of truncating, so several commands can build one log:

wc -l data.txt | tee run.log >/dev/null
wc -c data.txt | tee -a run.log >/dev/null
cat run.log
2 data.txt
8 data.txt

script -c runs a single command and exits, so it needs nobody to type exit and can be used inside a script of your own:

script -q -c 'wc -l data.txt' session.log >/dev/null
sed -n '2p' session.log
2 data.txt
false | tee out.log >/dev/null; echo "status: $?"
false | tee out.log >/dev/null; echo "PIPESTATUS: ${PIPESTATUS[0]}"
status: 0
PIPESTATUS: 1

${PIPESTATUS[0]} is the first command's own status. It is a bash array, so it is not available in /bin/sh. In a script that must catch the failure, set -o pipefail makes the pipeline report the first non-zero status instead, and exit codes and error handling sets out its exceptions.

A typescript keeps every control character a program sent, so colour codes and the cursor movements behind a progress bar all end up in the file looking like noise. cat -v shows what is there and col -b strips most of it. For a recording somebody is going to read, the shorter path is script -q and a command that does not colour its output.