Bending a pipeline back into its origin

There's an app for that! The sponge command from moreutils is designed for precisely this. If you are running Linux, it is likely already installed, if not search your operating system's repositories for sponge or moreutils. Then, you can do:

echo foo >a
cat a | rev | sponge a

Or, avoiding the UUoC:

rev a | sponge a

The reason for this behavior is down to the order in which your commands are run. The > a is actually the very first thing executed and > file empties the file. For example:

$ echo "foo" > file
$ cat file
$ > file
$ cat file

So, when you run cat a | rev >a what actually happens is that the > a is run first, emptying the file, so when the cat a is executed the file is already empty. This is precisely why sponge was written (from man sponge, emphasis mine):

sponge reads standard input and writes it out to the specified file. Unlike a shell redirect, sponge soaks up all its input before writing the output file. This allows constructing pipelines that read from and write to the same file.

  1. The output truncation is done very early, so cat sees an empty file.
  2. Either the first file is constructed as a temporary, or the output of rev is directed to a temporary which you then rename.

another way to fix this is to use a writing method that does not truncate

  rev a | dd conv=notrunc of=a

this only works because:

  1. rev reads content before producing output and the output is never longer than the amount already read

  2. the new file content is same size or larger than the original (in this case same size)

  3. dd opens the file to write without truncating it.

This approach may be useful for in-place modification of files too large to keep temporary copies of.