全体見積

全体見積

私はなぜこの3つのコマンドが3つの異なる答えを与えるのか尋ねました。

$ printf "%s\n"   `echo ../.. | sed 's/[.]/\\&/g'`
&&/&&
$ printf "%s\n"  $(echo ../.. | sed 's/[.]/\\&/g')
../.. 
$ printf "%s\n" "$(echo ../.. | sed 's/[.]/\\&/g')"
\.\./\.\.

答え1

これは答えにくい質問です。最後の説明の最も簡単な例です。

全体見積

実際に完全に引用された例は次のとおりです。

$ printf "%s\n"    "$(   echo "../.." | sed 's/[.]/\\&/g'   )"
\.\./\.\.

すべてが引用されるので、シェルはここで何のトリックも変更も行いません。最も内側の内容はecho "../.."引用されるため、ファイル名拡張の影響を受けません。変更せずにsedに移動し、sedは各点をに変更します\&。その後、コマンドの結果も引用さ"$(...)"れ(再び)シェルへの変更を防ぎ、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

globbing(ファイル名拡張)の影響を受けた場合は、../..GLOBIGNORE値が原因で削除されます。

ただし、ファイル名拡張子がないことを確認するために、存在しないファイル名に切り替えることができます。##/##

引用符なしでコマンドを実行する

\引用符なしで(コマンドが実行されても)シェルがaを削除する理由はありません。

$ printf '%s\n' $(printf '%s\n' '\#\#/\#\#')
\#\#/\#\#

実際、私がテストしたシェルのどれも、2番目の例で報告したものを示していません。 (修正してください!)

編集: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'`"
\#\#/\#\#

答え2

Bash バージョン 5.0.17 では、man bash2 つのコマンド置換形式の間に次の違いがあります。

   When  the  old-style  backquote form of substitution is used, backslash
   retains its literal meaning except when followed by $, `,  or  \.   The
   first backquote not preceded by a backslash terminates the command sub‐
   stitution.  When using the $(command) form, all characters between  the
   parentheses make up the command; none are treated specially.

set -xシェルにいる場合、違いを見ることができます:

$ set -x

$ printf "%s\n"   `echo ../.. | sed 's/[.]/\\&/g'`
++ echo ../..
++ sed 's/[.]/\&/g'
+ printf '%s\n' '&&/&&'
&&/&&

ディスプレイは\\&次のように縮小されました\&\ はい\);は sed&で特別な意味を失って、 while(新しいスタイル)に..なりました。&&

$ printf "%s\n"  $(echo ../.. | sed 's/[.]/\\&/g')
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' ../..
../..

2つのバックスラッシュは文字通りsedに渡され、sedはそれを..(文字通りのバックスラッシュであり、\.\.特別な意味を保持します)に置き換えます。\\&引用しない結果はシェルによって反映され、引用された../..コマンド置換がsed出力をそのまま印刷すると、次のようになります\.\./\.\.

$ printf "%s\n" "$(echo ../.. | sed 's/[.]/\\&/g')"
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' '\.\./\.\.'
\.\./\.\.

また、見ることができます...$(...)が(backtick)よりも優れているのはなぜですか?


Bashバージョン4.4.20では、引用符のないバージョンと引用符付きコマンドの代替バージョンが$(...)同じ結果を提供します。

$ echo $BASH_VERSION
4.4.20(1)-release

$ set -x

$ printf "%s\n"  $(echo ../.. | sed 's/[.]/\\&/g')
++ echo ../..
++ sed 's/[.]/\\&/g'
+ printf '%s\n' '\.\./\.\.'
\.\./\.\.

関連情報