我有这个有效的代码:
# 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
如果我尝试像这样缩短它:
# 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"
我收到错误消息:
[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 ...)
为什么硬编码参数有效,而变量参数无效?
编辑2:
目前我发现第二个答案的替代建议取得了成功:
# 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"
编辑1:
根据第一个答案,我应该指出程序头已经包含:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
答案1
不能通过扩展来引起重定向的原因是"$HideErrors"
,像>
参数扩展这其实非常好,因为这样的符号出现在您可能想要扩展并按字面意思使用的文本中。
无论你是否引用 ,这都成立$HideErrors
。参数扩展的结果取决于单词拆分和通配符当扩展名不加引号时,就是这样。
至于该怎么做,有很多方法可以实现条件重定向。对于一个非常简单的命令,将整个命令写两次可能是合理的,在或case
构造if
的每个分支中各写一次else
。然而,这很快就会变得繁重,而您展示的命令肯定不是理想的情况。
让你避免重复,我特别推荐其中两个,因为它们非常简洁,而且很容易正确使用。对于同一个命令和重定向,您只需要使用其中一个,而不是同时使用两个。
存储命令而不是重定向。不要尝试将重定向存储在变量中并应用参数扩展,而是将命令存储在shell 函数。然后编写一个case
或if
- else
,其中一个分支上使用重定向调用函数,而另一个分支不使用重定向调用函数。
如果你将命令概念化为想要编写一次但在多种情况下运行的代码,那么函数就是自然的解决方案。我通常这样做。它的好处是不需要子壳也不需要手动存储和重置状态。
使用您的代码:
launch() {
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName"
}
case $fCron in
true) launch 2>/dev/null;;
*) launch;; # Get silly error messages when running from terminal
esac
您可以应用任何您喜欢的间距,或者if
-else
如果您愿意的话。请注意,launch
自动使用调用者的RobWebAddress
和DownloadName
变量,即使它们是局部变量,因为 Bash 是动态范围,与大多数具有词汇范围的编程语言不同。
在子 shell 中运行命令并有条件地将重定向应用到exec
。这是什么steeldriver 评论了, 但保持(
)
局部效应。 什么时候内置exec
在没有参数的情况下运行,它不会用新进程替换当前 shell,而是将其任何重定向应用于当前 shell。
(也可以跟踪标准错误并恢复它,而不使用子shell,从而不牺牲修改当前shell环境的能力。不过,我将把细节留给其他答案。)
使用您的代码:
(
# Suppress silly error messages unless running from terminal
case $fCron in true) exec 2>/dev/null;; esac
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName"
)
在关闭之后)
,标准错误实际上恢复到之前的状态,因为它实际上只是在子 shell 中重定向,而不是在父 shell 中重定向。这也适用于现有的 shell 变量,因为子 shell 会获得这些变量的副本。虽然我更喜欢使用 shell 函数,但我承认这种方法可能需要更少的代码。
这两种方法都可以工作,无论标准错误从哪个文件或设备开始,包括将重定向应用于调用包含条件行为的代码的 shell 函数的情况,以及整个脚本的标准错误已被前一个或重定向的情况(在您的编辑中提到) 。路径是由exec 2>&fd
exec 2> path
流程替代没问题。
答案2
为什么硬编码参数有效,而变量参数无效?
因为语法项不是从扩展的变量值中解释的。也就是说,变量扩展与在命令行中用变量的文本替换变量引用不同。(诸如;
、|
和&&
引号等内容在变量值中也不特殊。)
您可以做的是使用别名,或者使用变量来保存重定向的目标。
别名是只是文本替换,所以他们能保存语法项,如运算符和关键字。在脚本中,您需要shopt expand_aliases
,因为默认情况下它们在非交互式 shell 中是禁用的。因此,这将打印2
(仅):
#!/bin/bash
shopt -s expand_aliases
alias redir='> /dev/null'
redir echo 1
alias redir=''
redir echo 2
(然后你也可以alias jos=if niin=then soj=fi
用芬兰语写下所有的 if 语句。我相信任何读过这个脚本的人都会喜欢你。)
或者,始终写入重定向,但仅使用变量控制目标。如果您不想更改输出去向,则需要一个无操作目标,但在实际上,添加/dev/stderr
这种情况下应该可以工作。2> /dev/stderr
不是无操作,因为 Linux 将打开的 fd 视为/proc/<pid>/fd
独立于原始文件。这会影响写入位置的定位,如果输出到常规文件,则会弄乱输出。
但它应该在追加模式下工作(或者如果 stderr 进入管道或终端):
#!/bin/sh
exec 2>/tmp/error.log
dst=/dev/null
ls -l /nosuchfile-1 2>> "$dst" # this doesn't print
dst=/dev/stderr
ls -l /nosuchfile-2 2>> "$dst"
ls -l /nosuchfile-3 2>> "$dst"
所以重复一遍:2> /dev/stderr
可以打破。
答案3
问题标题:“如何将 2>/dev/null 作为变量传递?”实际上可以使用以下方法完成eval
joshua@nova:/tmp$ X=">/dev/null"
joshua@nova:/tmp$ echo $X
>/dev/null
joshua@nova:/tmp$ eval echo $X
joshua@nova:/tmp$ eval echo hi
hi
joshua@nova:/tmp$ eval echo hi $X
joshua@nova:/tmp$ echo hi $X
hi >/dev/null
joshua@nova:/tmp$
所以我们可以重写为
# Hide irrelevant errors so chrome doesn't email us in cron
local HideErrors
local RobWebAddress2
local DownloadName2
[[ $fCron == true ]] && HideErrors="2>/dev/null"
RobWebAddress2='"$RobWebAddress"'
DownloadName2='>"$DownloadName"'
eval google-chrome --headless --disable-gpu --dump-dom \
$RobWebAddress2 $DownloadName2 "$HideErrors"
间接变量访问可防止在命令行的其余部分过早发生扩展。
变量中的双引号工作正常。
joshua@nova:/tmp$ X='"'
joshua@nova:/tmp$ Y='$X'
joshua@nova:/tmp$ eval echo $Y
"
joshua@nova:/tmp$