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