简体   繁体   English

如何在bash shell中重定向命令输出?

[英]how to redirect command output in bash shell?

I'm having a problem writing a small bash command. 我在编写小bash命令时遇到问题。 Basically I want to echo the wrapper command and redirect the output of the real command to a log file. 基本上,我想回显wrapper命令并将真实命令的输出重定向到日志文件。

Something like this in my .bashrc doesn't work -- the output still gets to the console. 我的.bashrc中的类似内容不起作用-输出仍然进入控制台。

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

But the following works -- the output is directed into the log file. 但是下面的工作-输出直接进入日志文件。

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

What is wrong with the first method? 第一种方法有什么问题? How to fix it? 如何解决?

Thanks! 谢谢!

Using eval works, but is bad practice for security reasons. 使用eval可以工作,但是出于安全原因,这是不好的做法。 The Right Thing, when you need to perform redirections inside code stored for reuse, is to define a function: 当您需要在存储以供重用的代码内执行重定向时,Right Thing是定义一个函数:

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

If you don't need redirections, then the right thing is an array: 如果你不需要重定向,那么正确的做法是一个数组:

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

This is safer, inasmuch as array contents won't go through a full eval pass. 这是更安全的,因为数组内容不会经过完整的评估传递。 Think about if you did cmd="something-with $filename" , and filename contained $(rm -rf /) . 考虑一下您是否使用cmd="something-with $filename"filename包含$(rm -rf /) If you used eval , this would run the rm command! 如果使用eval ,它将运行rm命令!


To provide a more specific example, this would hose your system if run as root: 为了提供一个更具体的示例,如果以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!)

On the other hand, this would be safe: 另一方面,这将是安全的:

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

...and it would still be safe even if you left out some of the quotes -- it would work wrong, but still not break your system: ...并且即使您省略了一些引号也仍然是安全的-这样做会出错,但仍不会破坏系统:

# 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!)

And this would be safe too: 这也是安全的:

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

For a more in-depth discussion, see BashFAQ #50 (on properly storing command sequences for reuse), and BashFAQ #48 (on why eval is dangerous). 有关更深入的讨论,请参阅BashFAQ#50 (关于正确存储命令序列以供重用)和BashFAQ#48 (关于为什么评估很危险)。

What is wrong with the first method? 第一种方法有什么问题?

When you include the redirection operators within a variable, the shell doesn't treat those as special. 当您在变量中包含重定向运算符时,shell不会将其视为特殊字符。 Instead those are considered as arguments to the program in question. 而是将它们视为有关程序的参数。

One solution is to make use of eval : 一种解决方案是利用eval

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

As an aside, the following is wrong: 顺便说一句,以下是错误的:

cmd = "some command";

You cannot have spaces around = in a variable assignment. 变量分配中不能在=周围有空格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM