nohup gedit &> /dev/null
is POSIX syntax and is the same as:
nohup gedit &
> /dev/null
That is run nohup gedit
in background and then do a > /dev/null
redirection without running a command.
nohup gedit >& /dev/null
is not POSIX syntax and is the csh
way to redirect both stdout and stderr to /dev/null. csh
doesn't have the 2>&1
operator as found in Bourne, so it's the only way csh
has to redirect stderr.
zsh
(as often) also provides with the csh
syntax, but it also supports the x>&y
fd duplication operator of the Bourne shell, which means there's a conflict there.
ls >&file
redirects ls
's stdout and stderr to file
, but if the file is 2
, you've got a problem as
ls >&2
means redirect stdout to the resource pointed to by fd 2 (dup(2, 1)
). So you need to write it:
ls >& ./2
if you wanted to redirect both the stdout and stderr of ls
into a file called 2
in the current directory; or use the standard syntax.
bash
initially did no understand >&
, but it introduced the &>
operator instead for that, breaking POSIX compliance in the process (though it's unlikely a script would use cmd &> xxx
).
ksh
copied that operator in ksh93t+ in 2009, mksh in R35 in 2008 (disabled in posix
mode) but not >&
.
bash
added support for >&
in 2.05.
busybox sh
added support for both &>
and >&
in 1.13 (2008).
Neither >&
nor &>
as meaning redirect stdout and stderr are POSIX/Bourne.
If you want to redirect both stdout and stderr portably, the syntax is
cmd > file 2>&1
You are right that you cannot use |
that way. The reason is that the shell has already looked for pipelines and separated them into commands before it does the variable substitution. Hence, |
is treated as just another character.
One possible work-around is to place the pipe character literally:
$ cmd="sort -n"
$ ls | $cmd
In the case that you don't want a pipeline, you can use cat
as a "nop" or placeholder:
$ cmd=cat
$ ls | $cmd
This method avoids the subtleties of eval. See also here.
A better approach: arrays
A more sophisticated approach would use bash
arrays in place of plain strings:
$ cmd=(sort -n)
$ ls | "${cmd[@]}"
The advantage of arrays becomes important as soon as you need the command cmd
to contain quoted arguments.
Best Answer
The difference may be seen via
strace
:In the backquote case,
ls /tmp
is passed as a single argument to the-c
tosh
, which runs as expected. Without this backquote, the command is instead word split whenwatch
runssh
which in turn runs the suppliedsh
, so that onlyls
is passed as the argument to-c
, meaning that the sub-subsh
will only run a barels
command, and lists the contents of the current working directory.So, why the complication of
sh -c ...
? Why not simply runwatch 'ls /tmp|wc -l'
?