A few weeks ago, I have seen a weird answer about the question "(How to) silently start task(s) in the background?". This solution seems incorrect (c.f. my answer) although the shell seems to start the task silently in the background.
I. Issue: can we actually redirect the shell standard error?
There is no explanation with the proposed solution and an analyze does not provide a reliable answer.
Below, you can see the code snippet.
# Run the command given by "$@" in the background
silent_background() {
if [[ -n $BASH_VERSION ]]; then
{ 2>&3 "$@"& } 3>&2 2>/dev/null
fi
}
The problematic command is { 2>&3 "$@"& } 3>&2 2>/dev/null
.
i) Analyze
The group of commands ({ ... }
) specifies two redirections (3>&2
and 2>/dev/null
) for one command (# Run the command given by "$@" in the background
). The command is an asynchronous list (cmd&
) having one redirection (2>&3
).
POSIX specification
Each redirection shall apply to all the commands within the compound command that do not explicitly override that redirection.
The standard error redirection 2>/dev/null
is overridden by the redirection associated to the asynchronous list 2>&3
.
Concrete cases
In the first case, the standard error is redirected to /dev/null
whereas, in the second case, the standard error of the command is still attached to the terminal.
prompt% { grep warning system.log& } 2>/dev/null
prompt% { 2>&3 grep warning system.log& } 3>&2 2>/dev/null
grep: system.log: No such file or directory
Below, we can see similar cases. In the first case, the standard output of the command is still attached to the terminal. In the second case, the standard output redirection of the command is modified: >echo.txt
is overridden by >print.txt
.
prompt% { >&3 echo some data...& } 3>&1 >echo.txt
[1] 3842
some data...
prompt% file echo.txt
echo.txt: empty
prompt% { >&3 echo some data...& } 3>print.txt >echo.txt
[1] 2765
prompt% file echo.txt
echo.txt: empty
prompt% cat print.txt
some data...
ii) Observations
As previously mentioned, the command seems to start silently in the background. More precisely, the notification about a background job, e.g. [1] 3842
, is not displayed.
The previous analysis implies that the redirects are superfluous since they may be canceled.
{ redir_3 cmd& } redir_1 redir_2
is equivalent to cmd&
.
iii) Interpretation
In my mind, the mentioned construct hides the notification because of a side effect.
Can you explain how that happens?
II. Hypothese(s)
Ilkkachu's answer and comments allowed some progression. Note that each process has its own file descriptors.
- The shell messages may be send on the shell standard error.
Another context: An asynchronous list executed in a subshell. The command g
does not exist.
prompt% ( g& )
g: command not found
Asynchronous commands, commands grouped with parentheses,..., are executed in a subshell environment that is a duplicate of the shell environment...
A subshell inherits the value of its standard error stream from its parent shell.
Therefore, in the previous case, their standard error streams should refer to the terminal. However, the job notification is not displayed whereas the shell error message is probably displayed on the shell standard error.
print [n] nnnnn
to terminal. Where $n$ and $nnnnn$ are numbers.( ( g & ) )
example is different from the one you ask about above, since the parenthesis set up a subshell, and there's no subshell in{ 2>&3 "$@"& } 3>&2 2>/dev/null
.()
still start a subshell, even if there's only one pair. The braces{}
don't. As far as I understand, Bash alwaysfork()
s when starting a subshell, and that makes background processes started from subshells a bit different, since they're not children of the main shell process. They also don't e.g. show in the output ofjobs
from the main shell, etc. (the point of the question you link to). Apparently Bash doesn't bother to print the job id line in that case either. That's a completely different situation from the{ foo & }
redirection dance.{ 2>&3 "$@" & } 3>&2 2>/dev/null
is to get rid of the job id output line, which the subshell( "$@" & )
doesn't print in the first place...