有没有办法通过变量重定向 stdout 和 stderr,例如在脚本中添加命令选项?
例如我有一个脚本:
#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
mkdir $OPT 123/123/123 $TEST
我可以看到 OPT 被替换为-p
没有任何问题,并且 bash 将其解释为选项。但重定向解释为目录名称。
$ ./test.sh
+ TEST='>/dev/null 2>&1'
+ OPT='-p -v'
+ mkdir -p -v 123/123/123 '>/dev/null' '2>&1'
mkdir: created directory `123/123'
mkdir: created directory `123/123/123'
mkdir: created directory `>/dev'
mkdir: created directory `>/dev/null'
mkdir: created directory `2>&1'
有没有办法说 bash,$VAR 是重定向,而不是目录名称。
附言。可能我走错了路,但我想从我的脚本中进行可选的详细或非详细输出。但即使在非详细模式下,我也需要一些输出,因此我不能仅从脚本内的某些命令重定向整个 stdout 和 stderr。
答案1
另一种解决方案可能如下:
#!/bin/bash
verbose=0
exec 3>&1
exec 4>&2
if ((verbose)); then
echo "verbose=1"
else
echo "verbose=0"
exec 1>/dev/null
exec 2>/dev/null
fi
echo "this should be seen if verbose"
echo "this should always be seen" 1>&3 2>&4
然后1>&3 2>&4
仅添加到您想要查看输出的命令。
答案2
“如何”在其他答案中已经得到很好的解释;我想解决OP代码不起作用的“原因”。
基调是输出重定向已标记前变量扩展。 重定向实际上是执行的变量扩展后(因此您可以将输出重定向到存储在变量中的文件名),但是 shell识别在变量扩展之前进行后续处理的重定向。
换句话说,一旦变量被扩展,考虑重定向字符(<
或>
等)就“太晚了”,因为 shell 已经识别出它将作为重定向处理命令字符串的哪些部分。
如需进一步阅读,请参阅下面列出的步骤 1 和 3:
LESS='+/^SIMPLE COMMAND EXPANSION' man bash
答案3
它不会将其解释为“目录名称”,>
而是被引用,因此它被按字面意思处理(更具体地说,您正在发送字符串>dev/null 2>&1
。解决此问题的唯一方法是使用eval
或生成一个新的 shell。
至于你的问题中提到的“详细”问题,只需这样做:
verbose=1
if (( verbose )); then
mkdir -v -p /foo
else
mkdir -p /foo > /dev/null 2>&1
fi
答案4
您的变量中有很多空格,这些空格将无法正确评估。您将需要使用eval
它来进行设置。
#!/bin/bash -x
TEST=">/dev/null 2>&1"
OPT='-p -v'
eval mkdir $OPT 123/123/123 $TEST
这将允许 $OPT 被拆分为两个参数(-p
和-v
),而不是一个(-p -v
),并且与 $TEST 相同。也更改为使用,因为当前目录中/dev/null
不太可能有目录。dev