How do `>` and `>>` work?
I tried to rev
a file and then pipe it to cat > same_file
but it was turning into a blank file.
While I tried rev file.txt | cat > file2.txt && mv file.txt file2.txt;
it worked.
Even rev file.txt | cat >> file.txt;
worked.
But when I tried rev file.txt | cat > file.txt
it failed.
Solution 1:
The basic thing you need to grasp in this case between both ways of redirecting (> and >>) is:
>
Redirects and overwrites the information where it was pointed to. This happens while receiving any information through pipe "|"
>>
Redirects and concatenates to the information it was pointed to. This happens while receiving any information through pipe "|"
In both cases if the file does not exist, it will be created instead. Only on ">>" will the information be concatenated if you run it again on the same file. With ">" you would simply overwrite everything you did on the first run.
But here is the deal when using the same input file as the output file. In that particular case, if you use ">" you would be removing the information the "input" part needs to parse, since the output file would be "overwriting it". So in:
rev file.txt | cat > file.txt
What is actually happening in "slow motion explanation" is:
-
rev
prepares to reverse the content offile.txt
and send it to pipe - While
rev
is sending the information to pipe, pipe streams it directly tocat
. - While
cat
is receiving the information it will automatically apply it to thefile.txt
it was set with. - The keyword here is "while", because everything is happening at the same time. Please see the excellent comments below by Emil to have a deeper understanding on this part.
-
cat
will not wait forrev
to pipe the whole file. It will simply start the minute the first part of the information gets to it, which means, depending on what symbol you used, it will open a connection tofile.txt
. - In this case since you used the > instead of >>, the shell will truncate the output file which means it will open and clear the information of
file.txt
while waiting for the new information to get to it. With >> it would open a connection withfile.txt
and wait for new information on the last line detected. - Since the information was already cleared in
file.txt
with >,rev
would try to do it's work and get nothing becausecat
deleted everything in preparation for the new information.
So why do the others work after reading the above. Because of this:
rev file.txt | cat > file2.txt && mv file.txt file2.txt
Here you are piping to cat which is sending the information to another file. In this case, the processed input file file.txt
is not the same as the output file file2.txt
. After that you are literally overwriting the whole file2.txt
with file.txt
, so all the process made by cat
was deleted. Basically the whole line could be simplified like cp file.txt file2.txt
because it is doing the same thing since file2.txt
at the end looses the rev
and is overwritten with the mv
command.
rev file.txt | cat >> file.txt
In this case you are concatenating the information to the same file. So it is only opening a connection to that file but not erasing the information as seen with a single >. The end result should be, the original information plus the reversed information.
Solution 2:
When the shell sees redirection, it opens the relevant files first, before executing any of the commands involved. Thus, when you do:
foo file.txt | bar > file.txt
The redirection to file.txt
causes it to be truncated before foo
is run and can read file.txt
. On a side note, this is why you cannot do:
sed 'blah' file.txt > file.txt
And why sed
has an in-place editing option.
Lastly, doing:
.. | cat > file.txt
is a useless use of cat, especially so if you are attempting to read from file.txt
earlier on.
If you want to reverse a file in-place, there are no shortcuts. You might be able to use the sed
or awk
tricks with in-place editing.