Difference between cat and ‘>’ to zero out a file

Are these two commands any different on how they go about zero-ing out files? Is the latter a shorter way of doing the former? What is happening behind the scenes?

Both

$ cat /dev/null > file.txt

$ > file.txt

yield

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

cat /dev/null > file.txt is a useless use of cat.

Basically cat /dev/null simply results in cat outputting nothing. Yes it works, but it’s frowned upon by many because it results in invoking an external process that is not necessary.
It’s one of those things that is common simply because it’s common.

Using just > file.txt will work on most shells, but it’s not completely portable. If you want completely portable, the following are good alternatives:

true > file.txt
: > file.txt

Both : and true output no data, and are shell builtins (whereas cat is an external utility), thus they are lighter and more ‘proper’.

 

Update:

As tylerl mentioned in his comment, there is also the >| file.txt syntax.

Most shells have a setting which will prevent them from truncating an existing file via >. You must use >| instead. This is to prevent human error when you really meant to append with >>. You can turn the behavior on with set -C.

So with this, I think the simplest, most proper, and portable method of truncating a file would be:

:>| file.txt

Method 2

In terms of portability:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Notes:

  1. except in sh or ksh emulation, for redirections without a command, in zsh, a default command is assumed (a pager for stdin redirection only, cat otherwise), that can be tuned with the NULLCMD and READNULLCMD variables. That’s inspired from the similar feature in (t)csh
  2. Redirections were initially not performed for : in UnixV7 as : was interpreted half-way between a comment leader and a null command. Later they were and like for all builtins, if the redirection fails, that exits the shell.
  3. : and eval being special built-ins, if the redirection fails, that exits the shell (bash only does that in POSIX mode).
  4. Interestingly, in (t)csh, that’s defining a null label (for goto), so goto '' there would branch there. If the redirection fails, that exits the shell.
  5. Unless/if the corresponding command is available in $PATH (: generally isn’t; true, cat, cp and printf generally are (POSIX requires them)).
  6. If the redirection fails, that exits the shell.
  7. If file is a symlink to an non-existing file however, some cp implementations like GNU’s will refuse to create it.
  8. The initial versions of the Bourne shell didn’t support redirecting builtins though

In terms of legibility:

(this section is highly subjective)

  • > file. That > looks too much like a prompt or a comment. Also the question I’ll ask when reading that (and most shells will complain about the same) is what output exactly are you redirecting?.
  • : > file. : is known as the no-op command. So that reads straight away as generating an empty file. However, here again, that : can easily be missed and/or seen as a prompt.
  • true > file: what has boolean to do with redirection or file content? What is meant here? is the first thing that comes to my mind when I read that.
  • cat /dev/null > file. Concatenate /dev/null into file? cat being often seen as the command to dump the content of the file, that can still make sense: dump the content of the empty file into file, a bit like a convoluted way to say cp /dev/null file but still understandable.
  • cp /dev/null file. Copies the content of the empty file to file. Makes sense, though someone not knowing how cp is meant to do by default might think you’re trying to make file a null device as well.
  • eval > file or eval '' > file. Runs nothing and redirects its output to a file. Makes sense to me. Strange that it’s not a common idiom.
  • printf '' > file: explicitly prints nothing into a file. The one that makes most sense to me.

In terms of performance

The difference is going to be whether we’re using a shell builtin or not. If not, a process has to be forked, the command loaded and executed.

eval is guaranteed to be built in all shells. : is built-in wherever it’s available (Bourne/csh likes). true is builtin in Bourne-like shells only.

printf is built-in most modern Bourne-like shells and fish.

cp and cat generally are not built-in.

Now cp /dev/null file does not invoke shell redirections, so things like:

find . -exec cp /dev/null {} ;

are going to be more efficient than:

find . -exec sh -c '> "$1"' sh {} ;

(though not necessarily than:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

).

Personally

Personally, I use : > file in Bourne-like shells, and don’t use anything other than Bourne-like shells these days.

Method 3

You might want to look at truncate, which does exactly that: truncate a file.

For example:

truncate --size 0 file.txt

This is probably slower than using true > file.txt.

My main point however is: truncate is intended for truncating files, while using > has the side effect of truncating a file.

Method 4

The answer depends a bit on what file.txt is, and how process write to it!

I’ll cite a common use case : you have a growing logfile called file.txt, and want to rotate it.

Therefore you copy, for example, file.txt into file.txt.save, then truncate file.txt.

In this scenario, IF the file is not opened by another_process (ex: another_process could be a program outputting to that file, for example a program logging something), then your 2 proposals are equivalent, and both work well (but the 2nd is prefered as the first “cat /dev/null > file.txt” is a Useless Use of Cat and also opens and reads /dev/null).

But the real trouble would be if the other_process is still active, and still has an open handle going to the file.txt.

Then, 2 main cases arise, depending on how other process opened the file :

  • If other_process opens it in the normal way, then the handle will be still pointing to the former location in the file, for example at offset 1200 bytes. The next write will therefore start at offset 1200, and thus you’ll have again a file of 1200bytes (+ whatever other_process wrote), with 1200 leading null characters! Not what you want, I presume.
  • If other_process opened file.txt in “append mode”, then each time it writes, the pointer will actively seek to the end of the file. Therefore, when you truncate it, it will “seek” until byte 0, and you won’t have the bad side effect! This is what you want (… usually!)

Note that this means you need, when you truncate a file, to make sure that all other_process still writing to that location have opened it in the “append” mode. Otherwise you’ll need to stop those other_process, and start them again, so they start pointing at the beginning of the file instead of the former location.

References : https://stackoverflow.com/a/16720582/1841533 for a cleaner explanation, and a nice short example of difference between normal and append mode logging at https://stackoverflow.com/a/984761/1841533

Method 5

I like this and use it often because it looks cleaner and not like somebody hit the return key by accident:

echo -n "" > file.txt

Should be a built-in too?


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x