为何Shell脚本中2>/dev/null重定向被当作参数处理?
为什么shell变量中的重定向会被当作参数传递?
问题重现
你遇到的问题是:尝试通过变量拼接的方式控制命令是否重定向错误输出时,2>/dev/null被当作普通参数传给了目标脚本,完全没起到重定向的作用。
先看你的测试脚本:printOutput.sh:
#!/bin/sh if [ $# -gt 0 ]; then echo "Wrong parameter $1"; exit; fi echo "stdout" echo "errout" >&2
test.sh:
#!/bin/sh cmdBase="./printOutput.sh" if [ -z $1 ]; then #Do not pipe cmd="$cmdBase" else #Pipe err cmd="$cmdBase 2>/dev/null" fi echo "`$cmd`"
运行./test.sh --verbose时,输出如下:
Wrong parameter 2>/dev/null
用sh -x调试能看到更清晰的执行过程:
+ cmdBase=./printOutput.sh + [ -z --verbose ] + cmd=./printOutput.sh 2>/dev/null + ./printOutput.sh 2>/dev/null + echo Wrong parameter 2>/dev/null Wrong parameter 2>/dev/null
原因分析
这是由shell命令的解析顺序决定的:
shell处理命令时,会先解析重定向、管道这类语法结构,之后才会进行变量展开。当你把./printOutput.sh 2>/dev/null赋值给变量cmd后,执行$cmd时,shell只会把这个字符串拆分成./printOutput.sh和2>/dev/null两个独立参数,把后者当作普通参数传递给脚本——因为重定向的解析步骤已经在变量展开前完成了,不会再回头处理变量里的重定向语法。
解决方案
这里有几种可靠的写法,能让重定向正常工作:
1. 使用eval强制重新解析命令字符串
eval会让shell把变量展开后的字符串当作完整命令行重新解析,这样重定向就能被正确识别:
修改test.sh中的执行部分:
echo "`eval $cmd`"
⚠️ 注意:如果你的参数来自不可信来源,eval可能会引入安全风险,要谨慎使用。
2. 用函数封装命令逻辑
函数是shell中更安全、清晰的复用命令逻辑的方式,shell会正确解析函数内部的重定向:
#!/bin/sh run_print_output() { if [ -z "$1" ]; then ./printOutput.sh else ./printOutput.sh 2>/dev/null fi } echo "$(run_print_output "$1")"
3. 直接条件执行(最推荐)
如果逻辑不复杂,直接分条件执行不同命令,完全避免变量拼接的问题:
#!/bin/sh if [ -z "$1" ]; then output=$(./printOutput.sh) else output=$(./printOutput.sh 2>/dev/null) fi echo "$output"
这种写法可读性最高,也没有安全隐患,是最推荐的方案。
内容的提问来源于stack exchange,提问作者HYK
相关产品推荐
相关产品推荐

