101

私は初めて 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} ) )
}

bashのドキュメントには次のように書かれています:

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

compgen -Wこれで、可能な選択肢の単語リストを含む文字列が期待されることは理解できたと思いますが、このコンテキストでは、「${name[@]} は name の各要素を個別の単語に展開する」という意味がわかりません。

簡単に言え${array[*]}ば、動作します。${array[@]}しません。その理由を知りたいのですが、正確に何が${array[@]}展開されるのかをよりよく理解したいと思います。

4

2 に答える 2

138

[@](これは、Kaleb Pederson の回答に対する私のコメントの拡張です。 vsのより一般的な扱いについては、その回答を参照してください[*]。)

bash (または同様のシェル) がコマンド ラインを解析するとき、コマンド ラインを一連の「単語」に分割します (後で混乱を避けるために「シェル単語」と呼びます)。通常、シェル ワードはスペース (またはその他の空白) で区切られますが、エスケープまたは引用符で囲むことにより、シェル ワードにスペースを含めることができます。二重引用符で囲まれた と 展開された配列[@]の違いは、配列の各要素が個別のシェル ワードとして扱われる一方で、配列のすべての要素がスペースで区切られた単一のシェル ワードになることです (またはの最初の文字が何であれ)。[*]"${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 と呼びます) を to に提供することcompgenは異なります。この-Wオプションは補完語のリストを取りますが、補完語をスペースで区切った単一のシェル語の形式でなければなりません。引数を取るコマンド オプションは、常に (少なくとも私の知る限り) 単一のシェル ワードを取ることに注意してください。オプション フラグ) を開始します。

さらに詳細に:

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 にあるもの) が与えられたと不平を言うと思いますが、どうやらそれらを無視しているようです。

于 2010-07-28T17:00:17.613 に答える
74

あなたのタイトルは${array[@]}${array[*]}(両方とも{})について尋ねますが、それからあなたは$array[*]$array[@](両方ともなし{})について尋ねます。これは少し混乱します。私は両方に(内で{})答えます:

配列変数を引用符で囲み@、添え字として使用すると、配列の各要素は、その$IFSコンテンツ内に存在する可能性のある空白(実際にはの1つ)に関係なく、完全なコンテンツに展開されます。アスタリスク(*)を添え字として使用すると(引用符で囲まれているかどうかに関係なく)、各配列要素のコンテンツをで分割して作成された新しいコンテンツに展開される場合があり$IFSます。

スクリプトの例を次に示します。

#!/bin/sh

myarray[0]="one"
myarray[1]="two"
myarray[3]="three four"

echo "with quotes around myarray[*]"
for x in "${myarray[*]}"; do
        echo "ARG[*]: '$x'"
done

echo "with quotes around myarray[@]"
for x in "${myarray[@]}"; do
        echo "ARG[@]: '$x'"
done

echo "without quotes around myarray[*]"
for x in ${myarray[*]}; do
        echo "ARG[*]: '$x'"
done

echo "without quotes around myarray[@]"
for x in ${myarray[@]}; do
        echo "ARG[@]: '$x'"
done

そして、これが出力です:

with quotes around myarray[*]
ARG[*]: 'one two three four'
with quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three four'
without quotes around myarray[*]
ARG[*]: 'one'
ARG[*]: 'two'
ARG[*]: 'three'
ARG[*]: 'four'
without quotes around myarray[@]
ARG[@]: 'one'
ARG[@]: 'two'
ARG[@]: 'three'
ARG[@]: 'four'

私は個人的に通常欲しいです"${myarray[@]}"。さて、あなたの質問の2番目の部分に答えるために、${array[@]}$array[@]

あなたが引用したbashドキュメントを引用します:

中括弧は、シェルのファイル名展開演算子との競合を避けるために必要です。

$ myarray=
$ myarray[0]="one"
$ myarray[1]="two"
$ echo ${myarray[@]}
one two

ただし、そうすると$myarray[@]、ドル記号はしっかりとバインドされるmyarrayため、の前に評価され[@]ます。例えば:

$ ls $myarray[@]
ls: cannot access one[@]: No such file or directory

ただし、ドキュメントに記載されているように、角かっこはファイル名を拡張するためのものなので、これを試してみましょう。

$ touch one@
$ ls $myarray[@]
one@

これで、ファイル名の拡張が拡張後に行われたことが$myarrayわかります。

$myarrayまた、添え字なしのもう1つの注意点は、配列の最初の値に展開されます。

$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]
于 2010-07-27T22:53:28.243 に答える