Win*_*nix 16 command-line bash redirect
我有这个有效的代码:
# 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
Run Code Online (Sandbox Code Playgroud)
如果我尝试像这样缩短它:
# 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"
Run Code Online (Sandbox Code Playgroud)
我收到错误消息:
[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 ...)
Run Code Online (Sandbox Code Playgroud)
为什么硬编码的参数可以工作,但不能将参数作为变量?
目前我发现第二个答案的替代建议取得了成功:
# 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"
Run Code Online (Sandbox Code Playgroud)
基于第一个答案,我应该指出程序头已经包含:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
Run Code Online (Sandbox Code Playgroud)
Eli*_*gan 20
您不能通过扩展导致重定向发生的原因"$HideErrors"是像这样的符号>在由参数扩展生成后没有被特殊处理。这实际上非常好,因为这样的符号出现在您可能想要扩展和按字面使用的文本中。
无论您是否引用,这都适用$HideErrors。当扩展不带引号时,参数扩展的结果会受到分词和通配符的影响,仅此而已。
至于如何处理,有很多方法可以实现条件重定向。对于一个非常简单的命令,将整个命令写两次可能是合理的,一次在caseor if-else结构的每个分支中。然而,这很快就会变得繁重,并且您显示的命令肯定是不理想的情况。
在让您避免重复自己的方法中,我特别推荐两种方法,因为它们非常干净且易于正确使用。对于相同的命令和重定向,您只想使用其中之一,而不是同时使用两者。
存储命令而不是重定向。与其尝试将重定向存储在变量中并应用参数扩展,不如将命令存储在shell 函数中。然后写一个caseor if- else,其中在一个分支上使用重定向调用该函数,而在另一个分支上不使用它。
如果您将命令概念化为您想编写一次但在多种情况下运行的代码,那么函数是自然的解决方案。这是我通常做的。它的优点是既不需要子shell,也不需要手动存储和重置状态。
使用您的代码:
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
Run Code Online (Sandbox Code Playgroud)
您可以应用任何您喜欢的间距,或者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"
)
Run Code Online (Sandbox Code Playgroud)
在结束之后),标准错误实际上恢复到之前的状态,因为它只是在子shell 中被重定向,而不是在父shell 中。这也适用于现有的 shell 变量,因为子 shell 获得了这些变量的副本。虽然我更喜欢使用 shell 函数,但我承认这种方法可能需要更少的代码。
无论文件或设备标准错误以什么开始,这两种方法都有效,包括应用于调用包含条件行为的代码的 shell 函数的重定向的情况,以及标准错误的情况(在您的编辑中提到)整个脚本已经被前一个或. 路径是由进程替换产生的,这是没有问题的。exec 2>&fdexec 2> path
为什么硬编码的参数可以工作,但不能将参数作为变量?
因为语法项不是从扩展变量值解释的。也就是说,变量扩展与在命令行中用变量的文本替换变量引用不同。(东西一样;,|,&&和报价等也都是在变量的值不是特别的。)
你可以做的是使用别名,或者使用变量来保存重定向的目标。
别名是只是一个文本替换,所以他们可以持有的语法项目,如运算符和关键字。在脚本中,您需要shopt expand_aliases,因为默认情况下它们在非交互式 shell 中被禁用。所以,这打印2(仅):
#!/bin/bash
shopt -s expand_aliases
alias redir='> /dev/null'
redir echo 1
alias redir=''
redir echo 2
Run Code Online (Sandbox Code Playgroud)
(你也可以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"
Run Code Online (Sandbox Code Playgroud)
所以重复一遍:2> /dev/stderr可以打破。