私はなぜこの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 bash
2 つのコマンド置換形式の間に次の違いがあります。
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' '\.\./\.\.'
\.\./\.\.