Programming for fun and profit

A blog about software engineering, programming languages and technical tinkering

Tue 11 June 2019

I/O redirection in bash

Posted by Simon Larsén in Tip of the Week   

Alright, so Tip of the Week has turned somewhat into "tip every two or three weeks". It turns out that it's pretty difficult to find the time to actually write something every week. but I'll keep trying. With that out of the way, let's head into the subject matter of this post: I/O redirection. We'll just have a look at the most basic but also most generally applicable use of redirection: taking the output from a program and storing it in a file.

Important: Files will both be created and clobbered in this TOTW. When trying this stuff out, first create a new directory and do everything in there, so you don't litter your filesystem with strange files, or accidentally overwrite something important.

Redirecting output

To set the stage, I'll be working in a directory with the following contents:

[tmp] $ ls
file1.txt  file2.txt  image1.png file2.txt

Redirecting output is fairly simple, and useful when you want to save the output of some command in a file. There are two primary ways of redirecting output: appending and truncating. Appending is the one I use the most, so let's start with that one.

Appending output redirection

With >>, we can make an appending redirect.

[tmp] $ ls >> ls_output.txt  # output from ls saved to output.txt
[tmp] $ cat ls_output.txt    # let's have a look... 
file1.txt
file2.txt
image1.png
image2.png
ls_output.txt
[tmp] $ ls >> ls_output.txt  # append new output
[tmp] $ cat ls_output.txt    # let's have a look again
file1.txt
file2.txt
image1.png
image2.png
ls_output.txt
file1.txt
file2.txt
image1.png
image2.png
ls_output.txt

There are three things to note here. First, the ls_output.txt file does not exist in the initial directory, and so it is created with the first redirect. Note however that ls_output.txt is present in the first redirected output from ls: ls_output.txt is actually created before ls is run as there needs to be an open file descriptor* to the file pass along.

** * ** A file descriptor can simply be thought of as a pointer to a file. There is no need to understand file descriptors intimately to use basic I/O redirection efficiently.

The second redirect is then appended to the file, which at that point already exists. And that pretty much sums up how an appending redirect functions: it appends output to the specified file if it exists, and creates a file with the output if it does not exist. I find that this is most often the functionality that I want, but in some cases, you want to re-create the file from scratch with each redirect. That can be achieved with a truncating redirect.

Note: You may note that the output of ls is formatted differently when output to the terminal, and when redirected to a file. ls checks whether the stdout file descriptor points to a terminal, or something else, and formats the output accordingly. The details are somewhat out of scope.

Truncating output redirection

Let's assume that we start over from the initial state of the directory, before ls_output.txt existed. We can then make a truncating redirect with >.

[tmp] $ rm ls_output.txt    # restore initial directory state
[tmp] $ ls > ls_output.txt  # make a truncating redirect
[tmp] $ cat ls_output.txt   # and inspect the results
file1.txt
file2.txt
image1.png
image2.png
ls_output.txt
[tmp] $ ls > ls_output.txt  # another truncating redirect
[tmp] $ cat ls_output.txt
file1.txt
file2.txt
image1.png
image2.png
ls_output.txt

If you did not know what truncating meant before, you can probably figure it out now. With a single >, the specified file is created if it does not exist, just like with >>, but it is entirely overwritten (truncated, clobbered) if it already does exist. I rarely use a truncating redirect, as it is an easy thing to accidentally truncate a file you did not mean to touch. I recommend to always use an appending redirect, unless you have a good reason to truncate the targeted file.

And that's it for this TOTW!