Беспорядок о $ {массив [*]} по сравнению с $ {массив} в контексте завершения Bash

Я пробую писать завершение Bash впервые, и я немного смущен приблизительно двумя способами разыменовать массивы Bash (${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 говорится:

На любой элемент массива можно сослаться с помощью $ {имя [нижний индекс]}. Фигурные скобки требуются, чтобы избегать конфликтов с операторами расширения имени файла оболочки. Если нижний индекс или '*', слово расширяется до всех членов имени массива. Эти нижние индексы отличаются только, когда слово появляется в двойных кавычках. Если слово дважды заключается в кавычки, $ {имя [*]} расширяется до отдельного слова со значением каждого участника массива, разделенного первым символом переменной IFS и $ {имя} разворачивает каждый элемент имени к отдельному слову.

Теперь я думаю, что понимаю это compgen -W ожидает строку, содержащую список слов возможных альтернатив, но в этом контексте я не понимаю то, что "$ {разворачивает имя}, каждый элемент имени к отдельному слову" означает.

Длинная короткая история: ${array[*]} работы; ${array[@]} не делает. Я хотел бы знать, почему, и я хотел бы понять лучше что точно ${array[@]} расширяется в.

73
задан Telemachus 27 July 2010 в 23:08
поделиться

2 ответа

(Это расширение моего комментария к ответу Калеба Педерсона - см. Этот ответ для более общей трактовки [@] 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, чтобы избежать путаницы со словами оболочки) для compgen отличается; опция -W принимает список составных слов, но он должен быть в форме одного слова-оболочки с составными словами, разделенными пробелами. Обратите внимание, что параметры команды, которые всегда принимают аргументы (по крайней мере, насколько мне известно), принимают одно слово оболочки - иначе не было бы способа узнать, когда заканчиваются аргументы параметра, а обычные аргументы команды (/ other флаги опций) begin.

Более подробно:

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" - единственное comp-word, прикрепленное к флагу -W, и первый реальный аргумент, который compgen примет в качестве завершаемой строки - "perl-two / usr / bin / perl". Я ожидал, что compgen будет жаловаться на то, что ему были предоставлены дополнительные аргументы ("-" и все, что есть в $ cur), но, очевидно, он просто игнорирует их.

93
ответ дан 24 November 2019 в 12:18
поделиться

Ваш заголовок спрашивает о ${массив[@]} против ${массив[*]}, но затем вы спрашиваете о $массив[*] против $массив[@], что немного запутывает. Я отвечу на оба вопроса:

Когда вы цитируете переменную массива и используете @ в качестве подстрочного индекса, каждый элемент массива расширяется до его полного содержимого, независимо от пробельных символов (фактически, одного из $IFS), которые могут присутствовать в этом содержимом. Когда вы используете звездочку (*) в качестве подскрипта (независимо от того, заключен он в кавычки или нет), он может расширяться до нового содержимого, созданного путем разбиения содержимого каждого элемента массива на $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[@]}". Теперь, чтобы ответить на вторую часть вашего вопроса, ${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 без подстрочного индекса расширяется до первого значения массива:

$ myarray[0]="one four"
$ echo $myarray[5]
one four[5]
49
ответ дан 24 November 2019 в 12:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: