Обычно двойные кавычки в Bash означают «сделать все, что заключено в кавычки, в одно слово, даже если в нем есть разделители». Но, как вы заметили, $@
ведет себя иначе, когда оно находится в двойных кавычках. На самом деле это хакерский анализ, который восходит к предшественнику Bash, оболочке Bourne, и это специальное поведение применимо только к этой конкретной переменной.
Без этого хака (я использую этот термин, потому что он кажется непоследовательным с точки зрения языка, хотя и очень полезен), для сценария оболочки будет трудно передать свой массив аргументов какой-либо другой команде, которая хочет такие же аргументы , У некоторых из этих аргументов могут быть пробелы, но как бы они передали их другой команде, чтобы оболочка не объединила их в одно большое слово или не обработала список и не разбила аргументы с пробелами?
Ну, вы могли бы передать массив аргументов, и оболочка Bourne действительно имеет только один массив, представленный $*
или $@
, чье количество элементов равно $#
, а элементы $1
, $2
и т. Д., Так называемые позиционные параметры.
Пример. Предположим, у вас есть три файла в текущем каталоге с именами aaa
, bbb
и cc c
(в третьем файле есть пробел в имени). Вы можете инициализировать массив (то есть вы можете установить позиционные параметры), чтобы они были именами файлов в текущем каталоге следующим образом:
set -- *
Теперь массив позиционных параметров содержит имена файлов. $#
, количество элементов три:
$ echo $#
3
И мы можем перебирать параметры позиции несколькими различными способами.
1) Мы можем использовать $*
:
$ for file in $*; do
> echo "$file"
> done
но это повторно разделяет аргументы в пробеле и вызывает echo четыре раза:
aaa
bbb
cc
c
2) Или мы можем поставить кавычки вокруг $*
:
$ for file in "$*"; do
> echo "$file"
> done
но это группирует весь массив в один аргумент и вызывает echo только один раз:
aaa bbb cc c
3) Или мы могли бы использовать $@
, который представляет один и тот же массив, но ведет себя по-разному в двойных кавычках:
$ for file in "$@"; do
> echo "$file"
> done
будет производить
aaa
bbb
cc c
, поскольку $ 1 = "aaa", $ 2 = "bbb", а $ 3 = "cc c" и "$@"
оставляет элементы нетронутыми. Если вы оставите кавычки около $@
, оболочка сгладит и повторно проанализирует массив, эхо будет вызвано четыре раза, и вы получите то же самое, что и с голым $*
.
Это особенно полезно в сценарии оболочки, где позиционные параметры - это аргументы, которые были переданы вашему сценарию. Чтобы передать те же самые аргументы какой-либо другой команде - без разбиения оболочки на пробельные символы - используйте "$@"
.
# Truncate the files specified by the args
rm "$@"
touch "$@"
В Bourne это поведение относится только к позиционным параметрам, потому что это действительно единственный массив, поддерживаемый языком. Но вы можете создавать другие массивы в Bash , и вы даже можете применить старый хак разбора к этим массивам, используя специальный синтаксис "${ARRAYNAME[@]}"
, чей знак at выглядит почти как миг для мистера Борна:
$ declare -a myarray
$ myarray[0]=alpha
$ myarray[1]=bravo
$ myarray[2]="char lie"
$ for file in "${myarray[@]}"; do echo "$file"; done
alpha
bravo
char lie
О, а что касается вашего последнего примера, что должна делать оболочка с "pre $@ post"
, где у вас есть $@
в двойных кавычках, но у вас есть и другие вещи там? Последние версии Bash сохраняют массив, добавляют текст перед $@
к первому элементу массива и добавляют текст после $@
к последнему элементу:
pre aaa
bb
cc c post