我有这个有效的代码:
# 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)
不能通过扩展导致重定向发生的原因是,在参数扩展产生后,不会对
"$HideErrors"
符号进行特殊处理。这实际上非常好,因为这些符号出现在您可能希望按字面扩展和使用的文本中。>
无论您是否引用,这都成立
$HideErrors
。参数扩展的结果会在扩展不加引号时进行分词和通配,但仅此而已。至于怎么做,有很多方法可以实现条件重定向。对于一个非常简单的命令,将整个命令编写两次可能是合理的,一次在
case
orif
-else
构造的每个分支中。但是,这很快就会变得繁琐,而您显示的命令肯定是不理想的情况。在让您避免重复自己的方法中,我特别推荐两种方法,因为它们非常干净且易于正确处理。您只想使用其中之一,而不是同时使用相同的命令和重定向。
存储命令而不是重定向。与其尝试将重定向存储在变量中并应用参数扩展,不如将命令存储在shell 函数中。然后写一个
case
orif
-else
,其中函数在一个分支上调用重定向,而在另一个分支上没有重定向。如果您将命令概念化为您想编写一次但在多种情况下运行的代码,那么函数是自然的解决方案。这是我通常做的。它的好处是既不需要子shell,也不需要手动存储和重置状态。
使用您的代码:
您可以应用您喜欢的任何间距,或者
if
-else
如果您愿意的话。请注意,即使它们是局部变量,它也会launch
自动使用调用者RobWebAddress
和变量,因为 Bash 是动态作用域的,这与大多数具有词法作用域的编程语言不同。DownloadName
在子 shell 中运行命令并有条件地将重定向应用到
exec
. 这是steeldriver 评论的,但在内部(
)
保持效果局部。当内置函数不带参数运行时,exec
它不会用新进程替换当前 shell,而是将其任何重定向应用到当前 shell。(也可以跟踪什么是标准错误并恢复它,而无需使用子外壳,因此不会牺牲修改当前外壳环境的能力。不过,我会将其细节留给其他答案。)
使用您的代码:
关闭之后
)
,标准错误实际上恢复到以前的状态,因为它只是在子shell中真正被重定向,而不是在父shell中。这也适用于现有的 shell 变量,因为子 shell 会获得这些变量的副本。虽然我更喜欢使用 shell 函数,但我承认这种方法可能需要更少的代码。无论文件或设备标准错误以什么开始,这两种方法都有效,包括应用于调用包含条件行为的代码的 shell 函数的重定向,以及标准错误的情况(在您的编辑中提到)整个脚本已经被以前的or重定向了。路径是由进程替换产生的,这没有问题。
exec 2>&fd
exec 2> path
因为语法项不是从扩展的变量值中解释的。也就是说,变量扩展与在命令行中将变量引用替换为变量的文本不同。(像
;
,|
和&&
引号之类的东西在变量的值中也不是特别的。)你可以做的是使用别名,或者使用变量来保存重定向的目标。
别名只是文本替换,因此它们可以包含句法项,例如运算符和关键字。在脚本中,您需要
shopt expand_aliases
,因为默认情况下它们在非交互式 shell 中被禁用。所以,这打印2
(仅):(你也可以
alias jos=if niin=then soj=fi
用芬兰语写下你所有的 if 语句。我相信任何阅读脚本的人都会喜欢你。)或者,始终编写重定向,但仅使用变量控制目标。在您不想更改输出位置的情况下,您需要一个无操作目标,
但实际上,添加/dev/stderr
在这种情况下应该可以工作。2> /dev/stderr
并不是一个空操作,因为 Linux 将打开的 fd/proc/<pid>/fd
视为独立于原始文件的方式。这会影响写入位置的定位,如果输出到常规文件,则会弄乱输出。不过,它应该在附加模式下工作(或者如果 stderr 进入管道或终端):
所以重复一遍:
2> /dev/stderr
可以打破。问题标题:“如何将 2>/dev/null 作为变量传递?” 这实际上可以使用
eval
所以我们可以重写为
间接变量访问可防止在命令行的其余部分过早发生扩展。
变量中的双引号工作得很好。