What is the differences between &> and 2>&1

Solution 1:

Bash's man page mentions there's two ways to redirect stderr and stdout: &> file and >& file. Now, notice that it says both stderr and stdout.

In case of this >file 2>&1 we are doing redirection of stdout (1) to file, but then also telling stderr(2) to be redirected to the same place as stdout ! So the purpose may be the same, but the idea slightly different. In other words "John, go to school; Suzzie go where John goes".

What about preference ? &> is a bash thing. So if you're porting a script, that won't do it. But if you're 100% certain your script will be only working on system with bash - then there's no preference

Here's an example with dash , the Debian Amquist Shell which is Ubuntu's default.

$ grep "YOLO" * &> /dev/null
$ grep: Desktop: Is a directory
grep: Documents: Is a directory
grep: Downloads: Is a directory
grep: Music: Is a directory
grep: Pictures: Is a directory
grep: Public: Is a directory
grep: Templates: Is a directory
grep: Videos: Is a directory
grep: YOLO: Is a directory
grep: bin: Is a directory

As you can see, stderr is not being redirected

To address your edits in the question, you can use if statement to check $SHELL variable and change redirects accordingly

But for most cases > file 2>&1 should work


In more technical terms, the form [integer]>&word is called Duplicating Output File Descriptor, and is a feature specified by POSIX Shell Command Language standard, which is supported by most POSIX-compliant and Brourne-like shells.

See also What does & mean exactly in output redirection?

Solution 2:

I would generally recommend following the Bourne-again SHell's way of doing things, since bash is arguably the most popular Unix shell out there. Bash typically uses either &> or 2>&1. IMHO, neither is "perfect", so I recommend forgetting about that nonsense. Realistically, which one you should use depends on what you are trying to do.

2>&1 merges stderr with stdout, which can be useful if, for example, you want to pipe stderr text. So, for example, if you want to see if a program prints a certain stderr message, but don't want your screen filled with (presumably) unimportant garbage, you could do something like program 2>&1 | grep crashed, which will search the stdout and stderr from a program called "program" for the word "crashed".

On the other hand, if you don't want a program to print anything at all, you could simply run program &> /dev/null, which will redirect both stderr and stdout to /dev/null, a special file which magically makes things disappear. Or, if you want to save the output of a program (perhaps to report a bug or something), you could redirect both stderr and stdout to a file: program &> log.txt will redirect all data to a file called "log.txt". If you wanted to, you could redirect the stdout and stderr via program 2> log.txt > log.txt or program 2>&1 | cat > log.txt, both of which would have the same effect as using &>. If you do something like program 2>&1 > file, only stdout will be redirected, but stderr can still be piped to another program, such as cat, which could be redirected as shown above. However, typing &> is easier than any of the above examples, since it involve typing fewer characters (and it's a bit easier for human beings to read). Do note that program 2> log.txt > log.txt might be more likely to work on non-bash shells.

PS: if you are worried about people using other shells, there's something you can add to be first line of your script called a "magic number", or "shebang". This is essentially a way to make sure other computers (particularly those running Unix-like operating systems) know which program to use to execute a script. Different scripts use different shebangs. A shebang for a bash script looks like this:

#!/bin/bash

If you use the above as the first line of a given script, bash will generally be used to execute said script. This will make it much more difficult for someone to accidentally execute the script with the wrong shell.

PS: I'm not going to lie: up till now, I didn't know one could use >&, but, as far as bash goes, it seems to do the same as &>. You learn something new everyday.