Short answer: the Linux cat command prints files to the terminal and joins them together: cat file shows one file, cat a.txt b.txt > all.txt concatenates two into a third. cat -n numbers the lines and cat -A makes tabs, line ends and Windows carriage returns visible. For long files, use less, head or tail instead.
cat /etc/os-release # print one file
cat part1.txt part2.txt > all.txt # join two files into a third
cat -A script.sh # show tabs (^I), line ends ($) and CR (^M)The outputs on this page come from Debian 12 with GNU coreutils 9.1 and bash 5.2. GNU coreutils is the cat on Debian, Rocky Linux and most other distributions; Ubuntu switched to the Rust rewrite uutils by default in 25.10, and cat --version shows which one you have. The macOS and BSD notes follow the FreeBSD cat man page, and the sudo cases the bash documentation. Home directory paths in the outputs are shown as /home/deploy.
cat cheat sheet
| Task | Command |
|---|---|
| Print a file | cat file.txt |
| Print several files, one after another | cat a.txt b.txt |
| Join files into a new file | cat a.txt b.txt > all.txt |
| Append a file to another | cat extra.txt >> all.txt |
| Number every line | cat -n file.txt |
| Number only non-empty lines | cat -b file.txt |
| Squeeze runs of empty lines into one | cat -s file.txt |
| Show tabs, line ends and control characters | cat -A file.txt |
| Create a file from what you type (Ctrl+D ends) | cat > notes.txt |
| Write several lines from a script | cat > file <<'EOF' ... EOF |
| Insert standard input between two files | echo '---' | cat a.txt - b.txt |
| Print a file last line first | tac file.txt |
| Read a gzip-compressed file | zcat file.gz |
Viewing a file with cat
cat reads each file you name and writes it to standard output, unchanged and in order. With no file name, or with - as a name, it reads standard input instead. That is the whole program; everything else is options and redirection.
1 PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
2 NAME="Debian GNU/Linux"
3 VERSION_ID="12"When a file in the list is missing, cat prints an error for that one, carries on with the rest, and exits with status 1, so a script can still tell that something went wrong:
$ cat part1.txt nope.txt part2.txt
alpha
beta
cat: nope.txt: No such file or directory
gamma
$ echo $?
1Pointing it at a directory gives cat: d: Is a directory. For a file you may not read, it says Permission denied; prefix the command with sudo for root-only files such as logs under /var/log.
Concatenating files: cat a b > c
The name comes from concatenate. List the files in the order you want them, and send the result to a new file with >, or add it to the end of an existing one with >>:
$ cat part1.txt part2.txt > all.txt
$ cat all.txt
alpha
beta
gamma
$ echo "--- header ---" | cat - part2.txt
--- header ---
gammaThe - stands for standard input, so you can put piped text before, between or after files. The same trick joins the pieces of a file cut up with split, because a shell glob sorts the parts in name order:
split -b 1M -d backup.tar.gz backup.tar.gz.part- # .part-00, .part-01, ...
cat backup.tar.gz.part-* > restored.tar.gz
sha256sum backup.tar.gz restored.tar.gz # both hashes must matchChecked with a 3 MB file in three parts: the joined copy had the same SHA-256 as the original. Where to send output and errors, and what 2>&1 does, is covered in redirecting stdout and stderr.
cat options: -n, -b, -s
A small nginx snippet with two empty lines, a tab and trailing spaces shows what each option changes:
1 server {
2 listen 80;
3
4
5 server_name example.com;
6 } 1 server {
2 listen 80;
3 server_name example.com;
4 } 1 server {
2 listen 80;
3
4 server_name example.com;
5 }-b overrides -n when you give both. Numbering is handy for pointing at a line (“line 5 is wrong”), but if you only need one line by number, sed -n '5p' file prints exactly that one; the sed command guide explains the addressing.
| Option | Meaning | GNU (Linux) | macOS / BSD |
|---|---|---|---|
| -n | Number all lines | yes | yes |
| -b | Number non-empty lines only | yes | yes |
| -s | Squeeze repeated empty lines | yes | yes |
| -E | Show $ at each line end | yes | no (use -e) |
| -T | Show tabs as ^I | yes | no (use -t) |
| -v | Show control characters as ^X and M- | yes | yes |
| -A | Same as -vET | yes | no (use -et) |
| -e / -t | -vE / -vT | yes | yes |
POSIX itself defines only -u (unbuffered output, which GNU cat accepts and ignores), so a script that has to run on any Unix should not rely on the others. cat -et is the spelling that works on both GNU and BSD systems.
cat -A: see tabs, trailing spaces and Windows line endings
server {$
listen 80;$
$
$
^Iserver_name example.com; $
}$Each line ends in $, so the two spaces before the $ on line 5 are trailing whitespace you could not see before, and ^I is a tab where the rest of the file uses spaces. That matters for YAML, Makefiles and Python, where a tab and four spaces are not the same thing.
The most useful case is a script that was saved on Windows:
#!/bin/bash^M$
echo hi^M$^M before every $ is a carriage return: the file has Windows (CRLF) line endings, which is what makes Linux answer bad interpreter: No such file or directory. sed -i 's/\r$//' win.sh or dos2unix win.sh converts it.
Creating and appending to files with cat
Type the content: cat > file
cat > notes.txt # type your lines, then press Ctrl+D on an empty line
cat >> notes.txt # the same, but adds to the end instead of replacing> empties the file first, without asking; >> appends. Ctrl+D sends end-of-file and cat exits. Ctrl+C also stops it, and whatever you had already entered on earlier lines is kept.
From a script: a heredoc
To write several lines from a script or a copy-paste, feed cat a here-document. Whether the word after << is quoted decides if variables are expanded:
name=world
cat > greeting.txt <<EOF
hello $name
home=$HOME
EOF
cat > template.txt <<'EOF'
hello $name
home=$HOME
EOFhello world
home=/home/deploy
hello $name
home=$HOMEUnquoted EOF expands $name, $HOME and $(...); quoted 'EOF' writes the text exactly as typed, which is what you want for a config file or a script that contains its own $ signs. >> with a heredoc appends, and <<-EOF strips leading tab characters (not spaces) so the block can be indented inside an if.
The trap: cat file > file empties it
$ cat notes.txt > notes.txt; echo "rc=$?"; wc -c notes.txt
rc=0
0 notes.txt
$ cat header.txt notes.txt > notes.txt
cat: notes.txt: input file is output fileThe shell truncates the output file before cat even starts. In the first line cat then reads an already empty file: no error, exit status 0, and the content is gone. In the second, GNU cat notices that it is reading the file it is writing and refuses with input file is output file, but by then the original content has already been thrown away. To prepend or rewrite, write to a temporary file and move it over:
cat header.txt notes.txt > notes.txt.new && mv notes.txt.new notes.txtUseless use of cat (and when it is fine)
Most commands that read text take a file name themselves, so cat file | grep error starts an extra process to do what grep error file does alone. It also costs information:
$ cat five.txt | wc -l
5
$ wc -l five.txt
5 five.txt
$ wc -l < five.txt
5Given a file name, wc and grep can print it: through a pipe, grep -l answers (standard input) instead of the file name, and tail -f cannot follow a pipe: it stops when the input ends. The < file form feeds a file to a command that only reads standard input. A cat at the front of a pipeline is still fine when it joins several files, or when it makes a long one-liner easier to read; on small files the difference is nothing you can measure. The grep command guide covers the flags that make the pipe unnecessary.
Big files: less, head and tail instead of cat
cat prints everything at once, so a 2 GB log scrolls past for minutes and leaves only the end on screen. Read big files with a pager, or take just the part you need:
less /var/log/syslog # page through; / searches, q quits
head -n 20 access.log # the first 20 lines
tail -n 50 error.log # the last 50 lines
tail -F error.log # follow a log as it grows
grep -n "timeout" error.log # only the lines you are looking for
zcat access.log.2.gz | less # a rotated, compressed logThe tail command guide explains why -F beats -f for anything under /var/log.
cat on a binary file, and fixing the terminal
cat a binary file and the terminal fills with symbols, may beep, and can be left printing line-drawing characters instead of letters, because some of those bytes are terminal control sequences. Type reset and press Enter (blind, if you have to) to reinitialise the terminal; stty sane fixes a terminal that no longer echoes what you type. Check a file before you print it, or look at it safely with -v:
$ file /usr/bin/cat
/usr/bin/cat: ELF 64-bit LSB pie executable, x86-64, ...
$ head -c 16 /usr/bin/cat | cat -v; echo
^?ELF^B^A^A^@^@^@^@^@^@^@^@^@tac: cat in reverse
$ tac five.txt
line 5
line 4
line 3
line 2
line 1tac (cat spelled backwards) prints the last line first, which suits logs where the newest entry is at the bottom: tac access.log | grep -m 1 ' 500 ' finds the most recent 500 without reading the whole file in order. It is part of GNU coreutils; macOS does not ship it, and tail -r does the same job there.
Emptying a file without deleting it
: > app.log # shortest form in bash and sh
truncate -s 0 app.log # explicit, also works with sudo
cat /dev/null > app.log # the classic spelling, same effectAll three leave an empty file with the same owner and permissions; each was checked down to 0 bytes. For a log that a running service keeps open this is the right move: deleting it with rm does not free the space until the service closes the file, as explained under deleted files that still use space.
cat on a server
- Quick facts about the machine:
cat /etc/os-releasefor the distribution,cat /proc/cpuinfoandcat /proc/meminfofor CPU and memory. The Linux version guide lists the other ways. - Before editing a config:
cat -Aon it shows whether a previous editor left tabs, CRLF line endings or trailing spaces behind. - Keys and certificates:
cat ~/.ssh/id_ed25519.pubprints a public key to paste into another server. Never cat a private key on a shared screen or into a log.
On a KVM VPS with Debian or Ubuntu you get GNU coreutils cat, the same implementation as in the examples here, and full root, so the sudo cases apply as written. More everyday commands sit in the Linux commands cheat sheet.
Frequently asked
What does the cat command do in Linux?
cat reads one or more files and writes them to standard output in order. With one file it simply prints it; with several it joins them, which is where the name, short for concatenate, comes from. Redirect the output with > to save the joined result as a new file.
How do I append one file to another with cat?
cat extra.txt >> main.txt adds the contents of extra.txt to the end of main.txt, while a single > would replace main.txt. Never redirect into a file that is also an input, such as cat a.txt main.txt > main.txt: the shell empties main.txt before cat reads it.
How do I see tabs and Windows line endings in a file?
Run cat -A file. It marks each line end with $, shows a tab as ^I and a Windows carriage return as ^M, so lines ending in ^M$ come from a file with CRLF line endings. On macOS and BSD, cat -et shows the same.
What is the opposite of cat?
tac prints a file with the last line first. It is part of GNU coreutils on Linux. macOS does not include it; tail -r does the same job there.