使用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是危險的)。
你的意思是「重定向」,而不是「管道」。另外,第二個代碼示例中的'.'不被視爲連接器,這似乎是你期望的(我猜你可能來自Perl或其他東西?);相反,它是從字面上打印的。只要刪除它。 (另外,正如devnull指出的那樣,你需要刪除'='周圍的空格。) –
已更新,你的猜測是非常正確的:-) – galactica
是的,在使用Bash時,Perlisms肯定會讓你感覺不舒服。我認爲最主要的是要明白,在Bash中,即使你沒有用引號括起來,你寫的所有東西都是一串字符串;引號只是將單詞集合保存爲單個字符串。所以'echo one two three''與echo'one two three'''或'echo one two three''相同,但'echo'one two three''是不同的,因爲引號會導致空格被保留。 –