Bash
有人能告訴我這三個命令替換之間的區別嗎?
我在問為什麼這三個命令會給出三個不同的答案:
$ printf "%s\n" `echo ../.. | sed 's/[.]/\\&/g'` &&/&& $ printf "%s\n" $(echo ../.. | sed 's/[.]/\\&/g') ../.. $ printf "%s\n" "$(echo ../.. | sed 's/[.]/\\&/g')" \.\./\.\.
這是一個棘手的問題。在最後一個中解釋的最簡單的例子。
完全引用
好吧,實際上,一個完全引用的例子:
$ printf "%s\n" "$( echo "../.." | sed 's/[.]/\\&/g' )" \.\./\.\.
這裡沒有任何技巧,也沒有 shell 所做的更改,因為所有內容都被引用了。最內部
echo "../.."
的被引用,因此不受文件名擴展的影響。它進入 sed 不變並且 sed 將每個點更改為 a\&
。然後該命令的結果也被引用"$(...)"
,這(再次)避免了 shell 的任何更改,並且該printf
命令也列印\.\./\.\.
. 這裡沒有驚喜。改成 ##/##
如果存在文件名擴展(萬用字元),
../..
我們../..
無論如何都會結束。所以,結束字元串是相同的。但是讓我們測試一下這個問題:$ echo ../../t* ../../test2.txt ../../tested ../../test.txt $ set -f # remove all filename expansion $ echo ../../t* ../.. ../../t* ../..
而且,無論如何,不包含 a
*
、 a?
或 a的字元串[
不受模式匹配表示法的約束證明:設置 GLOBIGNORE
$ (GLOBIGNORE='.*:..*'; echo "any" ../../t* ../.. "value") any ../.. value
如果
../..
受到萬用字元(文件名擴展)的影響,那麼它將由於 GLOBIGNORE 的值而被刪除。但是,為了確保沒有文件名擴展,我們可以切換到(很可能)不存在的文件名:
##/##
不帶引號的命令執行
\
即使(命令執行)未引用,shell 也沒有理由刪除 a :$ printf '%s\n' $(printf '%s\n' '\#\#/\#\#') \#\#/\#\#
事實上,我測試的所有 shell 都沒有顯示您在第二個範例中報告的內容。(請糾正我!)。
編輯:在 bash 5.0.17 中有一個錯誤。但只有點。
$ b50sh $ echo $BASH_VERSION 5.0.17(3)-release $ printf '%s\n' $(printf '%s\n' '\.\./\.\.') ../.. $ printf '%s\n' $(printf '%s\n' '\#\#/\#\#') \#\#/\#\# $ printf '%s\n' $(printf '%s\n' '\>\>/\>\>') \>\>/\>\> $ exit $ echo $BASH_VERSION 5.1.4(1)-release $ printf '%s\n' $(printf '%s\n' '\.\./\.\.') \.\./\.\. $ printf '%s\n' $(printf '%s\n' '\#\#/\#\#') \#\#/\#\#
似乎它已經在 bash 5.1.4 中解決了
反引號
這是最棘手的問題:內部反引號每個都
\\
變成\
.因此,sed 命令(如 sed 所見)是:
s/[#]/\&/g
. 要執行我認為您的意思是您需要複製\
s:$ printf '%s\n' `printf '%s\n' '##/##' | sed 's/[#]/\\&/g'` &&/&& $ printf '%s\n' "`printf '%s\n' '##/##' | sed 's/[#]/\\&/g'`" &&/&& $ printf '%s\n' "`printf '%s\n' '##/##' | sed 's/[#]/\\\\&/g'`" \#\#/\#\#