Bashはパラメータ拡張の文字列置換部分で引用をどのように処理しますか?

Bashはパラメータ拡張の文字列置換部分で引用をどのように処理しますか?

一貫したロジックがありますか?

some-command "${somevariable//some pattern/'how does this get parsed?'}"

以下にいくつかの結論と独創的なテストを「回答」として投稿しましたが、決して完全な答えではありません。 Bashのマニュアルページにはこのトピックが記載されていないようです。

答え1

コメントで説明したように、これはBashバージョン間で変更されたようです。これが適切な変化だと思いますbash-4.3-alpha変更ログ):

ZZ。パターン置換語拡張を使用すると、bash は文字列内の引用符をエスケープ文字として機能させることができるようになり、引用符を削除して置換文字列を実行します。これは以前のバージョンと互換性がないため、bash互換モードを4.2に設定して無効にすることができます。

しかもshopt -s compat42オンラインマニュアル):

compat42
設定すると、bash はパターン置換単語拡張で置換文字列を処理するために引用符の削除を使用しません。

一重引用符で引用する例:

$ s=abc\'def; echo "'${s//\'/\'\\\'\'}'"
'abc'\''def'

$ shopt -s compat42
$ s=abc\'def; echo "'${s//\'/\'\\\'\'}'"
'abc\'\\'\'def'

$ bash --version | head -1
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

回避策:代替文字列を変数に入れてからいいえ置換に引用符を使用します。

$ shopt -s compat42
$ qq="'\''"; s=abc\'def; echo "'${s//\'/$qq}'";
'abc'\''def'
$ qq="'\''"; s=abc\'def; echo "'${s//\'/"$qq"}'";
'abc"'\''"def'

興味深いことに、拡張が行われると引用しない、すべてのバージョンで置き換えられた後、引用符が削除されます。それがs=abc; echo ${s/b/""}版画だac。これは間違いなくs='a""c' ; echo ${s%x}出力などの他の拡張では発生しませんa""c

答え2

リバースエンジニアリングの一般的な規則:

  • 見積もりはリンクする必要があります(完了)。
  • 提案の維持(実際の交換に含まれる)
  • バックスラッシュが文字の前に表示されると、バックスラッシュが保持されます。
  • バックスラッシュが一重引用符をエスケープすると、バックスラッシュが保持されます。
  • 一重引用符内でも、バックスラッシュバックスラッシュシーケンスは1つのバックスラッシュに縮小されます。
  • 一重引用符の中に一重引用符をエスケープすることはできません。
  • パラメータ拡張は、一重引用符の外側と同様に、一重引用符の内側でも同じように機能します。
  • バックスラッシュを使用してドル記号をエスケープすると、ドル記号はそのまま残り、バックスラッシュは削除されます。

そして結論を​​下した:

  • '\''パラメータ拡張によって代替リテラルシーケンスを生成する方法はまったくありません。
  • ただし、代替テキストシーケンスを作成するの"'\''"は非常に簡単です。

以下はいくつかの生のテストです。

[vagrant@localhost ~]$ echo "$0"
-bash
[vagrant@localhost ~]$ echo "${0//a/x}"
-bxsh
[vagrant@localhost ~]$ echo "${0//a/some long string  with spaces}"
-bsome long string  with spacessh
[vagrant@localhost ~]$ echo "${0//a/"quoted string"}"
-b"quoted string"sh
[vagrant@localhost ~]$ echo "${0//a/"unfinished quote}"
> wat
> }"
-b"unfinished quote}"
wat
sh
[vagrant@localhost ~]$ echo "${0//a/\"escaped quote}"
-b"escaped quotesh
[vagrant@localhost ~]$ echo "${0//a/\\escaped escape}"
-b\escaped escapesh
[vagrant@localhost ~]$ echo "${0//a/\'escaped single quote}"
-b\'escaped single quotesh
[vagrant@localhost ~]$ echo "${0//a/''}"
-b''sh
[vagrant@localhost ~]$ echo "${0//a/''''}"
-b''''sh
[vagrant@localhost ~]$ echo "${0//a/'''}"
> a'b}c"d
-b'''}"
a'bshcd
[vagrant@localhost ~]$ echo "${0//a/'''}"
> w'x}y"z
-b'''}"
w'xshyz
[vagrant@localhost ~]$ echo "${0//a/'\'\\"a test'\'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/'\''\\"a test'\'}"
-b'\''\"a test'\'sh
[vagrant@localhost ~]$ echo "${0//a/'\''\\"a test'\$0'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/\\"a test'\$0'}"
> w}x"y
-b\"a test'$0'}"
wshxy
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\$0'}"
-b\"a test'$0'sh
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\\'$0'}"
> ^C
[vagrant@localhost ~]$ echo "${0//a/\\\"a test'\\$0'}"
-b\"a test'\-bash'sh
[vagrant@localhost ~]$ 

答え3

実験では、次の構文を使用すると、GNU bashバージョン5.0.11(1)リリース(x86_64-pc-linux-gnu)で一重引用符を二重引用符に変換できることがわかりました。

text="aaa'bbb'ccc"; echo "${text//$"'"/$'"'}"

明らかに...

aaa"bbb"ccc

一重引用符パターンは二重引用符で囲み、二重引用符置換パターンは一重引用符で囲む必要があります。

以下の関連ショッピング履歴があります。

extquote  
    If set, $'string' and $"string" quoting is performed within
    ${parameter} expansions  enclosed  in  double quotes.  This option
    is enabled by default.

関連情報