why it is 2>&1 and not 2>>&1 to append to a log file

43

6

I am redirecting STDOUT and STDERR to a single file, always growing, so I use always use "append" redirection; this is >>

my command is command >> logfile 2>&1

And it works.

But the STDERR redirection has a single >, which I was using to "create" the file, erasing the previous one, as in command > outlog 2> errlog

Why it does not erase the log file in this case?

Mikel Vergy

Posted 2019-05-06T10:50:06.320

Reputation: 521

9Welcome to Super User and congrats on an interesting first question! – gronostaj – 2019-05-06T11:04:02.130

14The tokens aren't split up the way you think they are. That is, it's not "> for redirect to file" and "&1 for which file to redirect to", it's ">& for redirect to file descriptor (which is not the same thing as a file)" and "1 for which descriptor to redirect to". So it's a completely different operation, and you shouldn't a priori expect to be able to transfer any intuition from > or >> to >&. (This is just a first approximation. In the second approximation, there are some implementation details shared between the three operators, so you can transfer some knowledge...) – Daniel Wagner – 2019-05-06T17:49:27.623

8@DanielWagner Please don't answer in comments. – David Richerby – 2019-05-07T15:38:48.010

Answers

54

When you redirect something to &number, you are not opening a new file at all; you're reusing an already open file along with whatever mode it was opened.

The numbers refer to "open file" handles (file descriptors). So there is no technical difference between how >& and >>& (and indeed <&) would work – they all just mean "clone the existing file descriptor using dup()".

That is, 2>&1 indicates that file descriptor #1 (which you previously opened for appending using >>logfile) is cloned into number #2. And yes, 2<&1 works identically.

Side technical note: Appending vs truncating is not an explicit action done by the shell; it's actually a mode the shell specifies when opening the file, and the rest is performed by the OS itself. For example, when you use > the shell doesn't manually erase the old contents, it just adds O_TRUNC when calling open(). Therefore, when open() isn't called at all, the previous mode remains unchanged.

user1686

Posted 2019-05-06T10:50:06.320

Reputation: 283 655

Is O_TRUNC something you run into when doing windows programming in C? – barlop – 2019-05-06T11:28:21.313

7@barlop: Yes; not exactly, but quite similar. If you develop on raw Win32 API, the CreateFile() function has TRUNCATE_EXISTING, which means the same thing as POSIX open()'s O_TRUNC, and you have DuplicateHandle() which is like dup(), even if Windows' stdin/stdout are a bit weirder and more special-cased. (If you develop on Mingw, you just get the same POSIX open(). And most higher level I/O libraries, like C stdio or Python io, have modes such as "w" "a" "r+" which directly translate to the same thing.) – user1686 – 2019-05-06T11:38:08.977

@grawity, your explanation suggests that command 2>&1 >> logfile will overwrite the file, which is not the case, could you elaborate that? In my experiment error output went to console and standard output went to the file. – Oliv – 2019-05-07T09:53:17.577

@Oliv: Does it? You're not using any "overwrite" redirection operator, you're just using the same "duplicate fd" and "append to file". – user1686 – 2019-05-07T10:40:54.133

1Note that "cloning" here is not "aliasing". Once 2>&1 clones fd 1 to fd 2, they're still two independent file descriptors that merely reference the same "open file". Further redirections of fd 1 do not affect fd 2. – user1686 – 2019-05-07T10:50:47.607

I think what it does is that it first redirects error output to console and then standard output to console. So the order matters. – Oliv – 2019-05-07T14:43:47.880

1Fun fact: O_APPEND is a special flag that's separate from just omitting O_TRUNC. It means any write automatically happens at the end of the file, even if other writers made the file longer since your last write. i.e. every write() becomes like an atomic combination of lseek(SEEK_END) + write(). You can open a file for overwrite without truncation using for example dd of=foo conv=notrunc – Peter Cordes – 2019-05-07T21:02:27.030

1Also, note that order is important... 2>&1 >> logfile will redirect stderr to the terminal, and then subsequently redirect the application's stdout to logfile. – Attie – 2019-05-07T21:11:55.953

14

The sequence command >> logfile 2>&1 has two redirection stages:

  • command >> logfile will append to the logfile
  • 2>&1 will redirect stderr to stdout (which itself appends to the logfile)

So command >> logfile 2>&1 will not truncate the log file, while command >>logfile 2>logfile would.

Eugen Rieck

Posted 2019-05-06T10:50:06.320

Reputation: 15 128

1What about command >>logfile 2>>logfile? – Zereges – 2019-05-06T12:36:42.157

@Zereges Probably gives an error because you try to open logfile for writing twice. One of the 2 "opens" should fail with an error along the lines of "file already open (or locked) by another process" – Tonny – 2019-05-06T22:39:45.317

7No, files on Unix-like systems are not locked merely by being open. (For that matter, e.g. Linux doesn't support mandatory locking at all.) – user1686 – 2019-05-07T07:14:21.033

1@Zereges: You'd get 2 separate file descriptors (not duplicates of each other) each with their own file position. But they're both opened in O_APPEND mode, so every write is like an atomic lseek(SEEK_END) + write(). So I think it would Just Work with the same end result as normal dup2() but probably slightly less efficient. – Peter Cordes – 2019-05-07T21:05:47.460