I have this code that works:
# Hide irrelevant errors so chrome doesn't email us in cron
if [[ $fCron == true ]] ; then
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" 2>/dev/null
else
# Get silly error messages when running from terminal
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName"
fi
If I try to shorten it like this:
# Hide irrelevant errors so chrome doesn't email us in cron
local HideErrors
[[ $fCron == true ]] && HideErrors="2>/dev/null"
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" "$HideErrors"
I get error messages:
[0826/043058.634775:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.672587:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.711640:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
(... SNIP ...)
Why does a hard-coded argument work but not an argument as a variable?
Edit 2:
Currently I found success with second answer's alternate suggestion:
# Redirect errors when cron is used to /dev/null to reduce emails
ErrorPipe=/dev/stderr
[[ $fCron == true ]] && ErrorPipe=/dev/null
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" 2>"$ErrorPipe"
Edit 1:
Based on the first answer, I should point out program header already contains:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
The reason you can't cause redirection to occur by expanding
"$HideErrors"
is that symbols like>
aren't treated specially after being produced by parameter expansion. This is actually very good, because such symbols appear in text you might want to expand and use literally.This holds whether or not you quote
$HideErrors
. The result of parameter expansion is subject to word splitting and globbing when the expansion is unquoted, but that's it.As for what to do about it, there are numerous ways to achieve conditional redirection. For a very simple command, it may be reasonable write the whole command twice, once in each branch of a
case
orif
-else
construct. This soon becomes burdensome, however, and the command you showed is certainly a case where that would not be ideal.Of the approaches that let you avoid repeating yourself, there are two I especially recommend, because they are quite clean and easy to get right. You'd want to use just one of these, not both at once for the same command and redirection.
Store the command instead of the redirection. Instead of attempting to store the redirection in a variable and applying parameter expansion, store the command in a shell function. Then write a
case
orif
-else
, in which the function is called with the redirection on one branch and without it on the other.If you conceptualize your command as code that you want to write once but run under multiple circumstances, then a function is the natural solution. This is what I usually do. It has the benefit of requiring neither a subshell nor manual storage and resetting of state.
With your code:
You can apply whatever spacing you like, or
if
-else
instead if you prefer. Note thatlaunch
automatically uses the caller'sRobWebAddress
andDownloadName
variables, even if they are local variables, because Bash is dynamically scoped, unlike most programming languages which are lexically scoped.Run the command in a subshell and conditionally apply the redirection to
exec
. This is what steeldriver commented about, but inside(
)
to keep the effect local. When theexec
builtin is run with no arguments, it doesn't replace the current shell with a new process, but instead applies any of its redirections to the current shell.(It's also possible to keep track of what standard error was and restore it, without using a subshell and thus without sacrificing the ability to modify the current shell's environment. I'll leave the details of that to other answers, though.)
With your code:
After the closing
)
, standard error is in effect restored to whatever it was before, because it's only really being redirected in the subshell, and not in the parent shell. This, too, works fine with the existing shell variables, since subshells get a copy of those. Although I prefer to use a shell function, I admit this method may require less code.Both methods work regardless of what file or device standard error starts out as, including in the case of redirections applied to shell functions that call the code that contains the conditional behavior, as well as the case (mentioned in your edit) where standard error for the whole script has already been redirected by a previous
exec 2>&fd
orexec 2> path
. That the path was produced by process substitution is no problem.Because syntax items aren't interpreted from expanded variable values. That is, variable expansion isn't the same as replacing the variable reference with the text of the variable in the command line. (Stuff like
;
,|
,&&
and quotes etc. are also not special in the values of variables.)What you could do is to use aliases, or use the variable to hold just the target of the redirection.
Aliases are just a text replacement, so they can hold syntactic items, like operators and keywords. In a script, you'd need to
shopt expand_aliases
, since by default they're disabled in non-interactive shells. So, this prints2
(only):(And you could also
alias jos=if niin=then soj=fi
and then write all your if-statements in Finnish. I'm sure anyone reading the script would love you.)Alternatively, write the redirection always, but control just the target with a variable. You'll need a no-op target for the case where you don't want to change where the output goes, though
butActually, adding/dev/stderr
should work in that case.2> /dev/stderr
isn't a no-op because of the way Linux treats fd's opened from/proc/<pid>/fd
as independent from the original. This affects the positioning of the write position and will mess up the output if it goes to a regular file.It should work in append mode, though (or if stderr goes to a pipe or to a terminal):
So to repeat:
2> /dev/stderr
can break.Question title: "How to pass 2>/dev/null as a variable?" This can actually be done using
eval
So we can rewrite as
Where the indirect variable access prevents expansion from happening too soon on the rest of the command line.
Double quotes in variables work just fine.