1. ホーム
  2. arrays

[解決済み] bash補完における${array[*]}と${array[@]}の混同について

2022-11-29 17:51:03

質問

初めてbash補完を書こうとしているのですが、bash配列の2つの参照方法について少し混乱しています( ${array[@]}${array[*]} ).

ここにコードの関連する塊があります(ちなみに、それは動作しますが、私はそれをよりよく理解したいと思います)。

_switch()
{
    local cur perls
    local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew}
    COMPREPLY=()
    cur=${COMP_WORDS[COMP_CWORD]}
    perls=($ROOT/perls/perl-*)
    # remove all but the final part of the name
    perls=(${perls[*]##*/})

    COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) )
}

バッシュの のドキュメントによると :

配列のどの要素も ${name[subscript]} を使って参照することができます。中括弧は、シェルのファイル名展開演算子との衝突を避けるために必要です。添え字が '@' または '*' の場合、単語は配列名のすべてのメンバに展開されます。これらの添え字は、単語が二重引用符で囲まれている場合のみ異なります。単語が二重引用符で囲まれている場合、${name[*]} は、各配列メンバの値を IFS 変数の最初の文字で区切った 1 つの単語に展開し、${name[@]} は name の各要素を別の単語として展開します。

今、私は理解したつもりですが compgen -W が可能な選択肢の単語リストを含む文字列を期待することは理解できたと思いますが、この文脈では "${name[@]} expands each element of name to a separate word" が何を意味するのか理解できていません。

長い話になりますが ${array[*]} が動作します。 ${array[@]} は動作しません。私はその理由を知りたいと思いますし、また、具体的にどのような ${array[@]} は何に展開するのか、もっとよく理解したいと思います。

どのように解決するのですか?

(これはKaleb Pedersonの回答に対する私のコメントの拡張です。 [@][*] .)

bash (または類似のシェル) がコマンドラインを解析するとき、それは一連の "単語" (後で混乱を避けるために "shell-words" と呼ぶことにします) に分割されます。 一般に、シェルワードはスペース(または他の空白文字)で区切られますが、スペースをエスケープするか引用符で囲めば、シェルワードに含めることができます。 以下の違いは [@][*] -二重引用符で囲まれた展開された配列は "${myarray[@]}" では配列の各要素が個別のシェルワードとして扱われるのに対し "${myarray[*]}" では配列のすべての要素が空白で区切られた一つのシェルワードになります (あるいは IFS の最初の文字が何であれ)。

通常は [@] の動作が必要です。 例えば perls=(perl-one perl-two) を使用し ls "${perls[*]}" -- と同じです。 ls "perl-one perl-two" という名前のファイルを探しますが、これは perl-one perl-two という名前のファイルを探しますが、これはおそらくあなたが望んだものではありません。 ls "${perls[@]}" と同じです。 ls "perl-one" "perl-two" と同じであり、その方が何か役に立つことをする可能性が高いです。

補完語のリスト (シェル語と混同しないように comp-words と呼ぶことにします) を compgen は異なります。 -W オプションは comp-words のリストを取りますが、 comp-words を空白で区切った一つのシェルワードの形式でなければなりません。 そうでなければ、オプションへの引数がいつ終わり、通常のコマンド引数 (/other オプションフラグ) がいつ始まるかを見分ける方法がありません。

より詳細には

perls=(perl-one perl-two)
compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

とは等価である。

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

...これは、あなたが望むことをします。 一方

perls=(perl-one perl-two)
compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}

とは等価である。

compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}

...これは完全にナンセンスです: "perl-one" は -W フラグに付けられた唯一の comp-word で、最初の本当の引数は -- compgen が補完する文字列として受け取るもの -- "perl-two /usr/bin/perl" です。 私は compgen が余分な引数 ("--" と $cur にあるもの) を与えられたと文句を言うと思っていましたが、どうやらそれは無視されるだけでした。