12

(!!)次の例でマークされている行には驚かされました。

log1 () { echo $@; }
log2 () { echo "$@"; }

X=(a b)
IFS='|'

echo ${X[@]}   # prints a b
echo "${X[@]}" # prints a b
echo ${X[*]}   # prints a b
echo "${X[*]}" # prints a|b
echo "---"
log1 ${X[@]}   # prints a b
log1 "${X[@]}" # prints a b
log1 ${X[*]}   # prints a b
log1 "${X[*]}" # prints a b (!!)
echo "---"
log2 ${X[@]}   # prints a b
log2 "${X[@]}" # prints a b
log2 ${X[*]}   # prints a b
log2 "${X[*]}" # prints a|b

これが私の動作の理解です:

  • ${X[*]}両方とも${X[@]}展開しますa b
  • "${X[*]}"に展開します"a|b"
  • "${X[@]}"に展開します"a" "b"
  • $*$@は、 と と同じ動作を${X[*]}${X[@]}ますが、内容がプログラムまたは関数のパラメーターであることを除きます。

これはbash manualで確認されているようです。

したがって、行log1 "${X[*]}"では、引用符で囲まれた式が "a|b" に展開され、log1 関数に渡されることを期待しています。この関数には、表示される単一の文字列パラメーターがあります。なぜ他の何かが起こるのですか?

あなたの答えがマニュアル/標準の参考文献に裏打ちされていれば素晴らしいと思います!

4

3 に答える 3

7

IFSの要素を結合するだけでなく${X[*]}、引用符で囲まれていない展開を分割するためにも使用され$@ます。の場合log1 "${X[*]}"、次のことが起こります。

  1. "${X[*]}"a|b期待どおりに展開されるため、 inside に$1設定されます。a|blog1
  2. $@(unquoted) を展開すると、結果の文字列は になりますa|b
  3. |引用符で囲まれていない展開は、( のグローバル値によりIFS)を区切り文字として単語分割されるため、 は とechoの 2 つの引数を受け取りaますb
于 2015-07-28T13:14:50.293 に答える
5

これ$IFSは、次のように設定されているため|です。

(X='a|b' ; IFS='|' ; echo $X)

出力:

a b

man bash言います:

IFS 展開後の単語分割に使用される内部フィールド セパレータ ...

于 2015-07-28T13:08:18.260 に答える
3

[Special Parameters[( http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 ) ] の POSIX 仕様セクションで見つけます。

@

1 から始まる定位置パラメーターに展開されます。展開が二重引用符内で発生し、フィールド分割 (フィールド分割を参照) が実行される場合、各位置パラメーターは個別のフィールドとして展開されます。元の単語 (展開されたパラメーターが単語内に埋め込まれていると仮定)、最後のパラメーターの展開は元の単語の最後の部分と結合されます。位置パラメータがない場合、'@' が二重引用符で囲まれている場合でも、'@' の展開によってゼロ フィールドが生成されます。

*

1 から始まる定位置パラメーターに展開されます。二重引用符で囲まれた文字列 (二重引用符を参照) 内で展開が発生すると、各パラメーターの値が IFS 変数の最初の文字で区切られた単一のフィールドに展開されるか、IFS が設定されていない場合は a で展開されます。IFS がヌル文字列に設定されている場合、これは設定を解除することと同じではありません。最初の文字が存在しないため、パラメーター値が連結されます。

したがって、引用されたバリアントから始めます(より単純です):

*展開が「IFS変数の最初の文字で区切られた各パラメーターの値を持つ単一のフィールドに[s]を展開する」ことがわかります。これがとa|bから得られる理由です。echo "${X[*]"log2 "${X[*]}"

@また、「各位置パラメーターは個別のフィールドとして展開される」ように展開が展開されることもわかります。これがとa bから得られる理由です。echo "${X[@]}"log2 "${X[@]}"

仕様テキストのフィールド分割に関する注記を見ましたか? 「フィールド分割 (フィールド分割を参照) が実行される場所」? ここが謎の鍵です。

引用符の外では、展開の動作は同じです。違うのはその後の展開です。具体的には、フィールド/単語の分割です。

問題を示す最も簡単な方法は、コードをset -x有効にして実行することです。

これで次のようになります。

+ X=(a b)
+ IFS='|'
+ echo a b
a b
+ echo a b
a b
+ echo a b
a b
+ echo 'a|b'
a|b
+ echo ---
---
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 a b
+ echo a b
a b
+ log1 'a|b'
+ echo a b
a b
+ echo ---
---
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 a b
+ echo a b
a b
+ log2 'a|b'
+ echo 'a|b'
a|b

ここで注意すべきことはlog1、最後のケース以外のすべてで が呼び出されるまでに、|すでになくなっているということです。

すでになくなっている理由は、引用符がないと、変数展開の結果(この場合は*展開) がフィールド/単語分割になるためです。そして、拡張されたフィールドを結合するためと、それらを再度分割するための両方IFSに使用されるため、フィールド分割によって飲み込まれます。|

そして、説明を終了するために(実際に問題になっている場合)、log1呼び出しで展開の引用されたバージョン(つまり、正しくlog1 "${X[*]}"展開される)でもこれが失敗する理由は、それ自体が展開の引用された展開を使用しないためです。関数内のof自体が単語分割されています (その場合と他のすべての場合で見られるように)。log1 "a|b"log1 @@echo a blog1log1

于 2015-07-28T13:14:26.130 に答える