如何动态使用输出重定向?

如何动态使用输出重定向?

我正在尝试向我的脚本添加调试选项。通常我想隐藏任何输出,例如警告等,所以我放置了>/dev/null 2>&1很多命令。

现在,当我想调试脚本时,我必须手动删除这些脚本,或者将每个命令放入if ... fi变量测试中$DEBUG

我认为放入>/dev/null 2>&1一个变量($REDIR)并写入command arg1 $REDIR就可以了。如果我想调试,我只需要留空即可$REDIR

但对我的外壳进行的简短测试告诉我,它不会那样工作:

~$ echo "bla" >/dev/null 2>&1
~$ REDIR=>/dev/null 2>&1
~$ echo "bla" $REDIR
bla
~$

由于显而易见的原因,使用"or 'around>/dev/null 2>&1不起作用。

那么为什么我的想法在这里行不通呢?我是否误解了将命令等放入变量中并调用它们?

答案1

重定向不是命令,因此您不能以这种方式执行它。如果你使用 ,你就可以完成它eval,但这会带来麻烦。

完成您想要做的事情的更好方法是拥有一个用于调试输出的函数:

function debugprint {
    if [ ! -z "$debug" ]; then
        echo "$1"
    fi
}

debugprint "$(echo 'bla' 2>&1)"

这会将标准错误重定向到标准输出,然后debugprint使用输出作为参数进行调用。现在,当您想要调试时,您需要做的就是设置$debug为非空。

当然,这也打开了一个(不同的)蠕虫罐头(与引用相关)。您可能只想使用set -x它,这可能足以满足您的调试需求,也可能不足以满足您的调试需求。

答案2

为此,我通常定义一个类似 的函数run。在大多数情况下,这可以正确处理带有空格和其他参数的参数。

#!/bin/bash

run() {
        if $DEBUG; then
                v=$(exec 2>&1 && set -x && set -- "$@")
                echo "#${v#*--}"
                "$@"
        else
                "$@" >/dev/null 2>&1
        fi
}

DEBUG=false
run echo "bla"

DEBUG=true
run echo "bla"
run printf "%s . %s . %s\n" bla "more bla" bla

输出:

$ bash debug.sh 
# echo bla
bla
# printf '%s . %s . %s\n' bla 'more bla' bla
bla . more bla . bla

答案3

在您的情况下, echo 被视为$REDIR字符串参数。你想要这样的东西:

~$ echo "bla" >/dev/null 2>&1
~$ REDIR='>/dev/null 2>&1'
~$ eval "echo bla $REDIR"
~$

然而,除非您试图进行快速而肮脏的黑客攻击,否则 Wouter Verhelst 有更好的解决方案(而且它实际上并不那么长或复杂)。

答案4

对于我编写的每个 shell 函数,我都会做同样的事情。

fn(){ 
    echo some normal stderr debug stuff             >&2     #if $DBG 2>stderr
    dd if="\$DBG/please/report/on/this/file"                #ditto
    echo I DEFINITELY need to handle this           >&3     #always stderr
    ( PATH=; ".some" oops I expect to handle )     2>&4     #always /dev/null
    echo and the regular stuff                              #always unaffected
}   4<>/dev/null 3>&2 2>&"$((${#DBG}?3:4))"

我喜欢这样有几个原因。

  1. 使用${#DBG}len 的求值总是可以保证>=0测试的整数值 - 无论$DBG实际包含什么。DBG=IFS=0例如,这使得数学安全,即使。

  2. 每次该函数运行时,它只需执行一次实际操作open()-/dev/null任何其他时间我都重定向到/dev/null我正在对#fd>&4.

  3. 调试输出 - 正如我可能启用的那样set -x- 默认情况下会转储交互式 shell 中的函数除非我明确地将环境变量设置$DBG为任何非空值。

    • $DBG不为空时,重定向的数学扩展将指向其自身 - 其计算结果为2>&3.
    • 但除此之外,它的计算结果为2>&4并因此转到打开/dev/null描述符。
    • 我通常不需要查看我已经确定并保存到的函数的 20 行执行跟踪~/.sh/fn/...,但如果我已经确定,它所参与的命令行可能完全是另一回事set -x
    • $DBG当为 null/未设置时也很方便,但是set -x 启用是因为它打开一个每个命令的 eval 出口,$PS4其中不会进入 stderr - 但#fd>&3仍然可以到达那里。
  4. 它以合理的方式实现继承。

    • 调用其他函数的函数不能$DBG以任何方式影响该值,这可能会使它们默认开始写入 stderr(如果它已经不能写入)。
    • 如果$DBG未设置或为 null,则它调用的所有子函数都会失去 Even#fd3的意义,除非它使用 - 调用它们child_fn 2>&3- 在这种情况下,它们将获得与父函数显式写入 stderr 相同的机会。
    • 即使已经设置,也未设置$DBG以安静这些子函数。$DBG
    • 而且它可以 $DBG这样就可以调用顶级函数它将启用默认的 stderr 输出。
  5. 我保留了 stderr 的副本#fd>&3,因此如果该函数必须它仍然可以在该描述符上显式写入 stderr(除上述情况外)

  6. 描述符自行关闭,不会影响 fds 2、3、4 的当前 shell 值,因为它们仅与包装该函数的复合命令相关联。

    • 清理{3,4}>&-是没有必要的。

相关内容