我在编写一个小型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";
在变量赋值中,=
周围不能有空格。