我正在尝试向我的脚本添加调试选项。通常我想隐藏任何输出,例如警告等,所以我放置了>/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))"
我喜欢这样有几个原因。
使用
${#DBG}
len 的求值总是可以保证>=0测试的整数值 - 无论$DBG
实际包含什么。DBG=IFS=0
例如,这使得数学安全,即使。每次该函数运行时,它只需执行一次实际操作
open()
-/dev/null
任何其他时间我都重定向到/dev/null
我正在对#fd>&4
.调试输出 - 正如我可能启用的那样
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
仍然可以到达那里。
- 当
它以合理的方式实现继承。
- 调用其他函数的函数不能
$DBG
以任何方式影响该值,这可能会使它们默认开始写入 stderr(如果它已经不能写入)。 - 如果
$DBG
未设置或为 null,则它调用的所有子函数都会失去 Even#fd3
的意义,除非它使用 - 调用它们child_fn 2>&3
- 在这种情况下,它们将获得与父函数显式写入 stderr 相同的机会。 - 它能即使已经设置,也未设置
$DBG
以安静这些子函数。$DBG
- 而且它可以放
$DBG
这样就可以调用顶级函数后它将启用默认的 stderr 输出。
- 调用其他函数的函数不能
我保留了 stderr 的副本
#fd>&3
,因此如果该函数必须它仍然可以在该描述符上显式写入 stderr(除上述情况外)。描述符自行关闭,不会影响 fds 2、3、4 的当前 shell 值,因为它们仅与包装该函数的复合命令相关联。
- 清理
{3,4}>&-
是没有必要的。
- 清理