Bash

有人能告訴我這三個命令替換之間的區別嗎?

  • March 26, 2022

我在問為什麼這三個命令會給出三個不同的答案:

$ 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'`"
\#\#/\#\#

引用自:https://unix.stackexchange.com/questions/694124