TL;DR:
Is there a common expectation how something_else should behave?
Yes, as a regular command. The shell doesn't understand command1 command2
as two commands, it's one command and one argument (even if it may be a name of a valid binary found in /bin
for example). It's the command1
that later runs command2
via exec()
syscall, not the shell.
Are there shell constructs that can disambiguate these commands (like which program's output is redirected)
No, someting_else
would not be treated in any special way. As said above, there's no construct other than one command and one argument. It also seems the confusion in this question is that both strace
and foo
are assumed to be run by the shell, but in fact it's a chain of parent-child processes.
How shell processes commands and redirections
Bourne-like shells ( that's bash
, dash
, ksh
) all follow POSIX rules on how commands are interpreted. POSIX specifies:'A "simple command" is a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator.' This can be put into a form:
[VAR=foo BAR=baz] command1 [arg1, arg2...] [ n>m ]
The general rule is that redirection applies to a command, which would be left most non-assignment word. In your example, strace
is the command and foo
is the argument to strace
. The redirections apply only to the command, not the arguments, that is to strace
. The foo
isn't run by the shell, but by strace
, in which case foo
becomes a child process of strace
. Similarly, somethingelse
would be treated as a command with foo
as its argument.
File descriptors
As far as non built-in commands go, they're a child process of the shell, and child processess inherit shell's file descriptors, so they generally don't manage redirections - they receive "prepackaged" destinations, so as far as strace
and 2>/dev/null
go, it probably wouldn't care what file descriptor 2 actually is (unless it's actively checking on the source code level). strace
will still write output to file descriptor 2, but shell already wired-up that file descriptor to point to /dev/null
.
Because file descriptors are inherited, this also explains why if you do something like strace stat noexist 2>stracelog.txt
both error streams from both strace
and stat
go into the same file. By contrast, some commands allow explicitly specifying destination as one of their options. Thus, with strace -o tracelog.txt stat noexist 2>stracelog.txt
you only have output from stat
in the stracelog.txt
file - even though file descriptor is inherited, now -o
flag is the property of strace
, and output is managed by the command even if the file descriptor is inherited.
This also gives us a small hint: in theory commands could "redirect" in the sense of duplicating file descriptors onto existing ones via dup2()
syscall, which is exactly the same mechanism shells would use, but as far as >
type of redirection symbols - that's still under shell control and hence only interpreted by the parent shell.
Why 2>/dev/null ?
It's generally accepted that diagnostic output goes to stderr
. In fact we already have two excellent posts on this topic:
watch
and strace
in your examples follow the convention, that's all. There's no requirement per se to specify 2>/dev/null
to hide output of such commands.
If you do want to specify redirections for the command that you pass as an argument, to strace
for example, you'd need to have a shell around it. For instance,
strace -f bash -c 'stat /etc/passwd nonexisting 2>/dev/null'
Notice the use of -f
flag, since if your focus is on tracing syscalls as applied to stat
command, you might not see them without -f
flag.
sh -c foo 2>/dev/null | tr ' ' '-'
? In general, a command might do what ever it needs to do to complete its task, so it's hard to expect anything aboutsomething_else
without knowing what it does. The same goes forwatch
andstrace
, but they have man pages and other documentation that tells us what they do. – ilkkachu Aug 15 '18 at 21:19something_else
in the way ur sample code does, it arranges forsomething_else
's fd, 2, to be open to a pseudo file that discards whatever's written to it. BTW, this convention causes fd 2 to be named standard error. – ctt Aug 17 '18 at 11:27something_else
conforming to the standard error convention. In reality, it could emit errors anywhere, but it should only emit them to file descriptor 2, the standard error stream. – ctt Aug 17 '18 at 11:28