簡體   English   中英

Bash在執行前將引號插入字符串

[英]Bash inserting quotes into string before execution

我設法在我正在處理的初始化腳本中跟蹤完成了一個奇怪的問題。 我在以下示例中簡化了問題:

> set -x                           # <--- Make Bash show the commands it runs
> cmd="echo \"hello this is a test\""
+ cmd='echo "hello this is a test"'
> $cmd
+ echo '"hello' this is a 'test"'  # <--- Where have the single quotes come from?
"hello this is a test"

為什么 bash 將那些額外的單引號插入到執行的命令中?

在上面的例子中,額外的引號不會引起任何問題,但它們確實讓我頭疼。

對於好奇,實際問題代碼是:

cmd="start-stop-daemon --start $DAEMON_OPTS \
    --quiet \
    --oknodo \
    --background \
    --make-pidfile \
    $* \
    --pidfile $CELERYD_PID_FILE
    --exec /bin/su -- -c \"$CELERYD $CELERYD_OPTS\" - $CELERYD_USER"

產生這個:

start-stop-daemon --start --chdir /home/continuous/ci --quiet --oknodo --make-pidfile --pidfile /var/run/celeryd.pid --exec /bin/su -- -c '"/home/continuous/ci/manage.py' celeryd -f /var/log/celeryd.log -l 'INFO"' - continuous

因此:

/bin/su: invalid option -- 'f'

注意:我在這里使用su命令,因為我需要確保在運行 celeryd 之前設置用戶的 virtualenv。 --chuid不會提供這個

因為當您嘗試執行命令時

$cmd

只有一層擴展發生。 $cmd包含echo "hello this is a test" ,它被擴展為 6 個以空格分隔的標記:

  1. echo
  2. "hello
  3. this
  4. is
  5. a
  6. test"

這就是set -x輸出向您顯示的內容:它在包含雙引號的標記周圍加上單引號,以便清楚各個標記是什么。

如果您希望將$cmd擴展為一個字符串,然后再次應用所有 bash 引用規則,請嘗試使用以下命令執行您的命令:

bash -c "$cmd"

或(正如@bitmask 在評論中指出的,這可能更有效)

eval "$cmd"

而不僅僅是

$cmd

使用 Bash 數組來實現您想要的行為,而無需求助於非常危險的(見下文) evalbash -c

使用數組:

declare -a CMD=(echo --test-arg \"Hello\ there\ friend\")
set -x
echo "${CMD[@]}"
"${CMD[@]}"

輸出:

+ echo echo --test-arg '"Hello there friend"'
echo --test-arg "Hello there friend"
+ echo --test-arg '"Hello there friend"'
--test-arg "Hello there friend"

小心確保你的數組調用被雙引號包裹; 否則,Bash 會嘗試執行相同的“最低限度安全”轉義特殊字符:

declare -a CMD=(echo --test-arg \"Hello\ there\ friend\")
set -x
echo "${CMD[@]}"
${CMD[@]}

輸出:

+ echo echo --test-arg '"Hello there friend"'
echo --test-arg "Hello there friend"
+ echo --test-arg '"Hello' there 'friend"'
--test-arg "Hello there friend"

旁白:為什么eval是危險的?

eval只有在您可以保證傳遞給它的每個輸入都不會意外改變eval下命令的工作方式時才是安全的。

示例:作為一個完全人為設計的示例,假設我們有一個腳本作為我們自動化代碼部署過程的一部分運行。 該腳本對一些輸入(在本例中為三行硬編碼文本)進行排序,並將排序后的文本輸出到一個基於當前目錄名稱命名的文件中。 與此處提出的原始 SO 問題類似,我們希望動態構造傳遞給 sort 的--output=參數,但由於 Bash 的自動引用“安全”功能,我們必須(必須?不是真的)依賴eval

echo $'3\n2\n1' | eval sort -n --output="$(pwd | sed 's:.*/::')".txt

在目錄/usr/local/deploy/project1/中運行此腳本會導致在/usr/local/deploy/project1/project1.txt中創建一個新文件。

所以不知何故,如果用戶要創建一個名為owned.txt; touch hahaha.txt; echo的項目子目錄owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo ,腳本實際上會運行以下一系列命令:

echo $'3\n2\n1'
sort -n --output=owned.txt; touch hahaha.txt; echo .txt

如您所見,這完全不是我們想要的。 但是你可能會問,在這個人為的例子中,用戶創建項目目錄owned.txt; touch hahaha.txt; echo是不是不太可能owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo ,如果他們可以的話,我們不是已經遇到麻煩了嗎?

也許吧,但是如果腳本不是解析當前目錄名稱,而是解析遠程git源代碼存儲庫分支的名稱,那該怎么辦? 除非您計划非常努力地限制或清理每個用戶控制的工件,這些工件的名稱、標識符或其他數據被您的腳本使用,否則請遠離eval

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM