2014-02-25 72 views
0

我遇到了編寫小型bash命令的問題。基本上我想回顯包裝器命令並將真實命令的輸出重定向到日誌文件。如何在bash shell中重定向命令輸出?

在我的.bashrc中這樣的東西不起作用 - 輸出仍然到達控制檯。

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

但是下面的工作 - 輸出被定向到日誌文件中。

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

第一種方法有什麼問題?如何解決它?

謝謝!

+0

你的意思是「重定向」,而不是「管道」。另外,第二個代碼示例中的'.'不被視爲連接器,這似乎是你期望的(我猜你可能來自Perl或其他東西?);相反,它是從字面上打印的。只要刪除它。 (另外,正如devnull指出的那樣,你需要刪除'='周圍的空格。) –

+1

已更新,你的猜測是非常正確的:-) – galactica

+0

是的,在使用Bash時,Perlisms肯定會讓你感覺不舒服。我認爲最主要的是要明白,在Bash中,即使你沒有用引號括起來,你寫的所有東西都是一串字符串;引號只是將單詞集合保存爲單個字符串。所以'echo one two three''與echo'one two three'''或'echo one two three''相同,但'echo'one two three''是不同的,因爲引號會導致空格被保留。 –

回答

2

第一種方法有什麼問題?

當您在變量中包含重定向操作符時,shell不會將它們視爲特殊操作符。相反,這些被視爲有關程序的論據。

一種解決方案是使eval使用:

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

順便說一句,下面是錯誤的:

cmd = "some command"; 

你不能有大約=空間的變量賦值。

+0

@Ashish上面的命令中的'&'不是在後臺發送它。 '>&output.log'等同於說'> output.log 2>&1'。 – devnull

+0

對不起..我誤解了它是bashrc這是一個交互式shell(不是登錄)的問題 – Ashish

+0

更新了並且非常感謝您的解釋@devnull!這解決了我的問題! – galactica

6

使用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 

這樣更安全,因爲數組內容不會經過完整的評估過程。想一想,如果你做了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(關於爲什麼eval是危險的)。

+0

+1對於使用邪惡'eval'的真正好解釋陷阱。 – anubhava