如何在bashshell中重定向命令输出



我在编写一个小型bash命令时遇到问题。基本上,我想回显包装器命令,并将实际命令的输出重定向到日志文件。

在我的.bashrc中这样的东西不起作用——输出仍然到达控制台。

cmd="some_command >& output.log";
echo $cmd;
$cmd;

但以下操作是有效的——输出被定向到日志文件中。

cmd = "some_command";
echo $cmd" >& output.log";
$cmd >& output.log;

第一种方法有什么问题?如何修复?

谢谢!

使用eval是有效的,但出于安全原因,这是一种糟糕的做法。当您需要在存储的代码中执行重定向以供重用时,正确的做法是定义一个函数:

cmd() { some_command &> output.log; } # define it
declare -p cmd                        # print it
cmd                                   # run it

如果不需要重定向,那么正确的方法是数组:

cmd=( something 'with spaces' 'in args' ) # define it
printf '%q ' "${cmd[@]}"; echo            # print it
"${cmd[@]}"                               # run it

这更安全,因为数组内容不会经过完整的eval过程。想想你是否执行了cmd="something-with $filename",而filename包含$(rm -rf /)。如果您使用eval,这将运行rm命令!


为了提供一个更具体的例子,如果以root身份运行,这将为您的系统提供软管:

# !!! I AM DANGEROUS DO NOT RUN ME !!!
evil_filename='/tmp/foo $(rm -rf /)'
cmd="echo $evil_filename"             # define it (BROKEN!)
eval "$cmd"                           # run it    (DANGEROUS!)

另一方面,这是安全的:

evil_filename='/tmp/foo $(rm -rf /)'
cmd=( echo "$evil_filename" )         # define it (OK!)
printf '%q ' "${cmd[@]}"; echo        # print it  (OK!)
"${cmd[@]}"                           # run it    (OK!)

即使你遗漏了一些报价,它仍然是安全的——它会出错,但仍然不会破坏你的系统:

# I'm broken, but not in a way that damages system security
evil_filename='/tmp/foo $(rm -rf /)'
cmd=( echo $evil_filename )           # define it (BROKEN!)
${cmd[@]}                             # run it    (BROKEN!)

这也是安全的:

evil_filename='/tmp/foo $(rm -rf /)'
cmd() { echo "$1"; }                  # define it (OK!)
cmd "$evil_filename"                  # run it    (OK!)

有关更深入的讨论,请参阅BashFAQ#50(关于正确存储命令序列以供重用)和BashFAQ#48。

第一个方法有什么问题?

当在变量中包含重定向运算符时,shell不会将这些运算符视为特殊运算符。相反,这些被认为是有问题的程序的论据。

一种解决方案是利用eval:

cmd="some command >& output.log";
eval $cmd;

顺便说一句,以下是错误的:

cmd = "some command";

在变量赋值中,=周围不能有空格。

相关内容

  • 没有找到相关文章

最新更新