Cat without cat on the commandline
Say you want to display the contents of a file on the command line. The first tool we most of us reach for is cat
, which does a fine job at just this.
But what happens when you are on a Linux machine and when you try to cat a file this happens:
$ cat file.txt
-bash: cat: command not found
or even:
$ cat file.txt
bash: fork: retry: No child processes
This post explores this idea and was a feature of this challenge where you needed to display a file’s contents without using any utility outside of the shell.
Using shell built-ins, redirection and subshell
Using the shell builtin read
you can display the contents of a file, without a forking a new process:
while read line; do
echo $line;
done <file.txt
From the help page for read
:
One line is read from the standard input, or from file descriptor FD if the -u option is supplied, and the first word is assigned to the first NAME,
In this example, the contents of file.txt are redirected to the STDIN of read
, which processes the input line by line, until it reaches the end of the file. read
also can take a file descriptor as its input instead of STDIN, so this will also work:
exec 3<file.txt # Assign file descriptor 3 for reading
while read -u 3 line; do
echo $line
done
This ends up being a lot more typing than just cat file.txt
. With the bash
or zsh
there is a another way to display a file’s contents without using cat
:
echo "$(<file.txt)"
this method uses redirection and command substitution, and is mentioned in the bash man page:
The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).
It’s faster, because you are not forking a cat, but does it matter? Probably not, and may not be clear to everyone what you are doing, but you can see a difference with a quick test on your shell:
$ time for n in {1..1000}; do \
echo $(</etc/resolv.conf) >/dev/null; \
done
real 0m0.977s
user 0m0.380s
sys 0m0.604s
$ time for n in {1..1000}; do \
cat /etc/resolv.conf >/dev/null;
done
real 0m1.980s
user 0m0.626s
sys 0m1.224s
This syntax $(<file.text)
may look a bit strange, what you are doing is command substitution, where the contents of file.txt
are sent to STDIN which is then echo’d as STDOUT. If you want to learn a bit more about redirection using >
and <
see my earlier post about shell redirection.
Using other utilities
How about other options? Without using shell built-ins but instead using other standard utilities you can also cat without cat:
ul < /file
ul
might inadvertently underline some words in your file but I think it might be the only way to cat a file with only two characters.
tac /file | tac
If you didn’t already guess, tac
is GNU core util that is the reverse of cat
so if you want to be clever you can pipe the output of tac
to tac
which is just a cat
.
Of course using tools like sed
, perl
, python
, etc. will allow you to cat files as well, happy cat’ing!
Interested in other ways to cat without cat? Try the oops challenge!