Bash 关闭stderr并将消息重定向到stdout

Bash 关闭stderr并将消息重定向到stdout,bash,shell,tcl,stdout,stderr,Bash,Shell,Tcl,Stdout,Stderr,我正在使用TCL Expect,并且正在脚本中执行一个脚本。是否可以关闭stderr流并将所有其他消息写入stdout set runcmd [ exec $SCRIPTS_PATH/config $build_tag -u 2>&- >&stdout] # Just in case... if { [catch $runcmd res] } { send_user "Failed to run command due to:

我正在使用TCL Expect,并且正在脚本中执行一个脚本。是否可以关闭stderr流并将所有其他消息写入stdout

    set runcmd [ exec $SCRIPTS_PATH/config $build_tag -u 2>&- >&stdout]

    # Just in case...
    if { [catch $runcmd res] } {
       send_user "Failed to run command due to: $res"
    }

上述代码的当前行为不会向stdout显示任何内容,但stderr会被避免,也不会显示。

要抑制stderr,可以执行以下操作:

exec config $build_tag -u 2>/dev/null

要抑制stderr,可以执行以下操作:

exec config $build_tag -u 2>/dev/null

要抑制stderr,可以执行以下操作:

exec config $build_tag -u 2>/dev/null

要抑制stderr,可以执行以下操作:

exec config $build_tag -u 2>/dev/null
Tcl(和expect)
exec
命令定义了它自己的重定向:请参阅——您不能仅仅假设sh/bash重定向可以工作

你想要

if { [catch {exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null} res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果要将stderr发送到stdout,请使用
2>@1
(参见手册页)

如果要将命令声明为变量,请执行以下操作:

set runcmd [list exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null]

if { [catch $runcmd res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果确实要使用shell重定向,则必须在shell中执行以下命令:

set runcmd [list exec sh -c "$SCRIPTS_PATH/config $build_tag -u 2>&1"]
我看不出
2>&-
在bash手册页中是有效的:
nTcl(and expect)
exec
命令定义了自己的重定向:请看——您不能仅仅假设sh/bash重定向会起作用

你想要

if { [catch {exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null} res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果要将stderr发送到stdout,请使用
2>@1
(参见手册页)

如果要将命令声明为变量,请执行以下操作:

set runcmd [list exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null]

if { [catch $runcmd res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果确实要使用shell重定向,则必须在shell中执行以下命令:

set runcmd [list exec sh -c "$SCRIPTS_PATH/config $build_tag -u 2>&1"]
我看不出
2>&-
在bash手册页中是有效的:
nTcl(and expect)
exec
命令定义了自己的重定向:请看——您不能仅仅假设sh/bash重定向会起作用

你想要

if { [catch {exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null} res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果要将stderr发送到stdout,请使用
2>@1
(参见手册页)

如果要将命令声明为变量,请执行以下操作:

set runcmd [list exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null]

if { [catch $runcmd res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果确实要使用shell重定向,则必须在shell中执行以下命令:

set runcmd [list exec sh -c "$SCRIPTS_PATH/config $build_tag -u 2>&1"]
我看不出
2>&-
在bash手册页中是有效的:
nTcl(and expect)
exec
命令定义了自己的重定向:请看——您不能仅仅假设sh/bash重定向会起作用

你想要

if { [catch {exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null} res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果要将stderr发送到stdout,请使用
2>@1
(参见手册页)

如果要将命令声明为变量,请执行以下操作:

set runcmd [list exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null]

if { [catch $runcmd res] != 0 } {
   send_user "Failed to run command due to: $res"
}
如果确实要使用shell重定向,则必须在shell中执行以下命令:

set runcmd [list exec sh -c "$SCRIPTS_PATH/config $build_tag -u 2>&1"]

我看不出
2>&-
在bash手册页中是有效的:
n如果我运行的命令想要向我想要禁止的stderr输出无关垃圾,我会使用
2>/dev/null
放弃它

# The “list” is important! It says “build a list here” instead of direct evaluation
set runcmd [list $SCRIPTS_PATH/config $build_tag -u]
exec {*}$runcmd 2>/dev/null
如果
$SCRIPTS\u PATH/config
具有非零退出代码,则仍然会产生错误,但这通常是正确的。要抓住它:

if {[catch { exec {*}$runcmd 2>/dev/null }]} {
    # An error has been trapped
}
Tcl不提供一种在子流程的stderr完全关闭的情况下运行子流程的方法;这种状态真的很奇怪。重定向到/dev/null更有可能是有用和明智的

另一方面,如果您希望在运行时将错误发送到外部Tcl脚本的stderr,而不会在生成这些消息时导致错误(有时是
exec
的一个非常令人恼火的特性)-因此
exec
的任何错误都只来自子进程退出代码-这是通过不同的重定向完成的,像这样:

exec {*}$runcmd 2>@stderr
set runcmd "$SCRIPTS_PATH/config $build_tag -u"
exec /bin/sh -c $runcmd 2>/dev/null
这是因为
exec
通常会捕获子流程stderr以用作更好的错误消息源。由于一些命令使用stderr打印错误消息,但实际上没有正确地输出错误,而另一些命令将日志信息写入stderr,而不仅仅是错误消息,这一切变得更加复杂。(这有点像一片沼泽,是许多人长期拼凑代码的结果。Tcl只是试图在这个领域做它能做的;我不相信它做得对,但过去的设计选择被现在依赖它们的脚本数量所认可。)


我不会把
exec
或重定向放在
列表
命令调用中;我认为这太令人困惑了。我更喜欢直接表达“属于Tcl的东西”。这只是口味的问题,但我认为更清楚。它还允许我考虑构建命令,然后将其发射给下级炮弹进行评估;内部语法不同(shell,而不是Tcl),但您可以执行以下操作:

exec {*}$runcmd 2>@stderr
set runcmd "$SCRIPTS_PATH/config $build_tag -u"
exec /bin/sh -c $runcmd 2>/dev/null
这在逻辑上是相当等价的,而且在某些方面更好


如果您仍在使用8.4(或更早版本!),您将无法使用
{*}
语法。如果
$runcmd
是由
list
构造的,那么作为替代方法,您可以这样做

eval exec $runcmd 2>/dev/null
理论上,你应该写:

eval [list exec] [lrange $runcmd 0 end] [list 2>/dev/null]
但是这太糟糕了,而且只有当
$runcmd
可能不是规范格式列表时才需要这样做。(我们在8.5中添加了列表扩展语法,因为我们再也不想看到那种怪物了,因为我们发现在任何情况下都很难做到足够小心,草率行事会导致错误。)


如果您使用的是8.4,并且没有严格要求您遵守它,请至少升级到8.5;8.4不再支持。(我们支持它已经有十多年了……)8.5与8.4的兼容性非常强,但值得检查一下您的代码是否真的有效。小心是值得的。如果遇到问题,请询问有关堆栈溢出的问题,当然要解决它。尽管如此,我的脚本在我迁移它们时都能正常工作,所以值得一试。

如果我运行的命令想要向我想要禁止的stderr输出无关垃圾,我会使用
2>/dev/null
放弃它

# The “list” is important! It says “build a list here” instead of direct evaluation
set runcmd [list $SCRIPTS_PATH/config $build_tag -u]
exec {*}$runcmd 2>/dev/null
如果
$SCRIPTS\u PATH/config
具有非零退出代码,则仍然会产生错误,但这通常是正确的。要抓住它:

if {[catch { exec {*}$runcmd 2>/dev/null }]} {
    # An error has been trapped
}
Tcl不提供一种在子流程的stderr完全关闭的情况下运行子流程的方法;这是一个很好的例子