When the output of a command is redirected to a file, the output file is created or truncated by the shell before the command is executed, any idea what cat foo > foo does?
The file is truncated first and then read, so this command would truncate the file.
When I tried to execute it I got this warning:
cat: test.txt: input file is output file
On Fedora 13 this is what I see
cat foo > foo
cat: foo: input file is output file
If foo
contained anything previously, it is gone.
Shell will truncate, cat
apparently checks a named parm, but will not check against stdin
as below and always succeeds:
$ uname -a
Linux bar 2.6.18-164.15.1.el5PAE #1 SMP Wed Mar 17 12:14:29 EDT 2010 i686 i686 i386 GNU/Linux
$ dd if=/dev/urandom of=foo bs=1024 count=4
4+0 records in
4+0 records out
4096 bytes (4.1 kB) copied, 0.00334456 seconds, 1.2 MB/s
$ od -c foo |head -2
0000000 U 371 003 z 224 334 z K 236 221 k < c 256 !
0000020 % % 256 V \ 005 , 254 X 202 330 004 222 " 037 226
$ cat <foo >foo && od -c foo
0000000
In all cases, the file is truncated. That’s because redirection is handled by the shell, which opens the file for writing before invoking the command.
cat foo > foo
The shell truncates and opens foo for writing, sets stdout to the file, and then exec’s ["cat", "foo"]
.
GNU cat is smart and refuses to redirect a file to itself. It does this by checking the device/inode pair on the input and output file descriptors; you can read the wonderful low-level details in src/cat.c. It prints a message and quits.
BSD cat doesn’t have such a safety, but since the file has already been truncated, there is nothing to read, nothing to write, and it will halt.
You can spice things up a bit by appending instead of truncating.
echo hello > foo
cat foo >> foo
Now everything is the same except the shell opens the file for appending instead of truncating it.
GNU cat sees what you’re doing and stops; the file is untouched.
BSD cat goes into a loop and appends the file to itself until your disk fills up.