Когда они не указаны, $*
и $@
являются одинаковыми. Вы не должны использовать ни один из них, потому что они могут неожиданно прерваться, как только у вас появятся аргументы, содержащие пробелы или символы подстановки.
"$*"
расширяется до одного слова "$1c$2c..."
. Обычно c
это пробел, но на самом деле это первый символ IFS
, так что это может быть что угодно.
Единственное хорошее применение, которое я когда-либо нашел для этого:
объединить аргументы с запятой (простая версия)
join1() {
typeset IFS=,
echo "$*"
}
join1 a b c # => a,b,c
объединить аргументы с указанным разделителем (лучшая версия)
join2() {
typeset IFS=$1 # typeset makes a local variable in ksh (see footnote)
shift
echo "$*"
}
join2 + a b c # => a+b+c
"$@"
разворачивается на отдельные слова: "$1"
"$2"
...
Это почти всегда то, что вы хотите. Он расширяет каждый позиционный параметр до отдельного слова, что делает его идеальным для ввода аргументов командной строки или функции и передачи их другой команде или функции. И поскольку он расширяется с использованием двойных кавычек, это означает, что вещи не ломаются, если, скажем, "$1"
содержит пробел или звездочку ( *
).
Давайте напишем скрипт , svim
который работает vim
с sudo
. Мы сделаем три версии, чтобы проиллюстрировать разницу.
svim1
#!/bin/sh
sudo vim $*
svim2
#!/bin/sh
sudo vim "$*"
svim3
#!/bin/sh
sudo vim "$@"
Все они подойдут для простых случаев, например, одного имени файла без пробелов:
svim1 foo.txt # == sudo vim foo.txt
svim2 foo.txt # == sudo vim "foo.txt"
svim2 foo.txt # == sudo vim "foo.txt"
Но только $*
и "$@"
работать правильно, если у вас есть несколько аргументов.
svim1 foo.txt bar.txt # == sudo vim foo.txt bar.txt
svim2 foo.txt bar.txt # == sudo vim "foo.txt bar.txt" # one file name!
svim3 foo.txt bar.txt # == sudo vim "foo.txt" "bar.txt"
И только "$*"
и "$@"
работать правильно, если у вас есть аргументы, содержащие пробелы.
svim1 "shopping list.txt" # == sudo vim shopping list.txt # two file names!
svim2 "shopping list.txt" # == sudo vim "shopping list.txt"
svim3 "shopping list.txt" # == sudo vim "shopping list.txt"
Так что только "$@"
все время будет работать правильно.
typeset
как сделать локальную переменную в ksh
( bash
и ash
использовать local
вместо). Это означает, IFS
что будет восстановлено его предыдущее значение, когда функция вернется. Это важно, потому что команды, которые вы запускаете позже, могут не работать должным образом, если IFS
установлено что-то нестандартное.
$*
. Я всегда считал, что это совершенно бесполезно ... соединение с разделителем - хороший вариант использования.Краткий ответ: используйте
"$@"
(обратите внимание на двойные кавычки). Другие формы очень редко полезны."$@"
довольно странный синтаксис. Он заменяется всеми позиционными параметрами, как отдельные поля. Если позиционных параметров нет ($#
равен 0), то"$@"
расширяется до нуля (не пустой строки, а списка с 0 элементами), если есть один позиционный параметр, то"$@"
эквивалентно"$1"
, если есть два позиционных параметра, то"$@"
эквивалентно"$1" "$2"
, и т.д."$@"
позволяет передавать аргументы скрипта или функции другой команде. Это очень полезно для упаковщиков, которые делают такие вещи, как установка переменных среды, подготовка файлов данных и т. Д. Перед вызовом команды с теми же аргументами и параметрами, с которыми вызывалась оболочка.Например, следующая функция фильтрует выходные данные
cvs -nq update
. Помимо фильтрации выходных данных и состояния возврата (то есть статуса,grep
а не статусаcvs
), вызовcvssm
некоторых аргументов ведет себя как вызовcvs -nq update
с этими аргументами."$@"
расширяется до списка позиционных параметров. В оболочках, которые поддерживают массивы, существует аналогичный синтаксис для расширения до списка элементов массива:"${array[@]}"
(скобки обязательны, за исключением zsh). Опять же, двойные кавычки несколько вводят в заблуждение: они защищают от разбиения поля и генерации шаблона элементов массива, но каждый элемент массива оказывается в своем собственном поле.В некоторых древних оболочках имелась ошибка: когда не было позиционных аргументов, они
"$@"
расширялись до одного поля, содержащего пустую строку, а не до поля. Это привело к обходному пути${1+"$@"}
( ставшему известным благодаря документации Perl ). Затрагиваются только более старые версии фактической оболочки Bourne и реализации OSF1, но ни одна из ее современных совместимых замен (ash, ksh, bash и т. Д.) Не затрагивается./bin/sh
не затрагивается ни одной системой, выпущенной в 21-м веке, о которой я знаю (если не считать техобслуживание Tru64, и даже если/usr/xpg4/bin/sh
это безопасно, так что#!/bin/sh
затрагиваются только сценарии, а не#!/usr/bin/env sh
сценарии, если ваш PATH настроен на соответствие POSIX) , Короче говоря, это исторический анекдот, о котором вам не нужно беспокоиться."$*"
всегда расширяется до одного слова. Это слово содержит позиционные параметры, соединенные пробелом между ними. (В более общем случае разделитель - это первый символ значенияIFS
переменной. Если значениеIFS
- пустая строка, разделитель - пустая строка.) Если позиционных параметров нет, то"$*"
это пустая строка, если есть два позиционные параметры иIFS
имеет значение по умолчанию, то"$*"
есть эквивалентное"$1 $2"
и т. д.$@
и$*
внешние кавычки эквивалентны. Они расширяются до списка позиционных параметров, как отдельные поля, например"$@"
; но каждое результирующее поле затем разделяется на отдельные поля, которые обрабатываются как шаблоны подстановочных знаков имени файла, как обычно с расширениями переменных без кавычек.Например, если текущий каталог содержит три файла
bar
,baz
аfoo
затем:источник
"$@"
действительно расширился список, состоящий из пустой строки: unix.stackexchange.com/questions/68484/…Вот простой скрипт, демонстрирующий разницу между
$*
и$@
:Выход:
В синтаксисе массива нет разницы при использовании
$*
или$@
. Это имеет смысл только тогда, когда вы используете их с двойными кавычками"$*"
и"$@"
.источник
IFS="^${IFS}"
?IFS
.IFS="^xxxxx"
сделать? Конечный${IFS}
суффикс заставил меня думать, что вы делаете что-то более хитрое, например, как-то автоматически восстанавливать исходный IFS в конце (например: первый символ автоматически смещается или что-то в этом роде).Код, который вы предоставили, даст тот же результат. Чтобы понять это лучше, попробуйте это:
Вывод теперь должен быть другим. Вот что я получаю:
Это сработало для меня
bash
. Насколько я знаю, ksh не должен сильно отличаться. По сути, цитирование$*
будет обрабатывать все как одно слово, а цитирование$@
будет рассматривать список как отдельные слова, как видно из приведенного выше примера.В качестве примера использования
IFS
переменной с$*
рассмотрим этоЯ получаю это в результате:
Кроме того, я только что подтвердил, что работает так же
ksh
. И то,bash
и другое былоksh
протестировано здесь под OSX, но я не понимаю, как это будет иметь большое значение.источник
unset IFS
в конце, чтобы сбросить его до оригинала, но у меня это не сработало, иecho $IFS
я получил стандартный вывод, который я получаю из него. УстановкаIFS
фигурных скобок вводит новую область видимости, поэтому, если вы не экспортируете ее, это не повлияет на внешнюю сторонуIFS
.echo $IFS
ничего не доказывает, потому что оболочка видит,
, но затем делит слова, используяIFS
! Попробуйecho "$IFS"
.IFS
должен решить это.IFS
не было другого пользовательского значения перед вызовом функции. Но да, в большинстве случаев отключение IFS будет работать.Разница важна при написании скриптов, которые должны правильно использовать позиционные параметры ...
Представьте себе следующий звонок:
Здесь всего 4 параметра:
В моем случае
myuseradd
это просто оболочка,useradd
которая принимает те же параметры, но добавляет квоту для пользователя:Обратите внимание на призыв
useradd "$@"
, с$@
цитатой. Это будет уважать параметры и отправлять их как естьuseradd
. Если вы хотите снять кавычки$@
(или использовать$*
также без кавычек), useradd увидит 5 параметров, так как 3-й параметр, содержащий пробел, будет разделен на две части:(и наоборот, если бы вы использовали
"$*"
, useradd бы видеть только один параметр:-m -c Carlos Campderrós ccampderros
)Короче говоря, если вам нужно работать с параметрами, относящимися к параметрам из нескольких слов, используйте
"$@"
.источник
// человек Баш . Кш, издалека, похожее поведение.
источник
Говоря о различиях между
zsh
иbash
:С кавычками
$@
и$*
,zsh
иbash
ведут себя одинаково, и я думаю, что результат является вполне стандартным среди всех оболочек:Без кавычек результаты одинаковы для
$*
и$@
, но различны вbash
и вzsh
. В этом случаеzsh
показано некоторое странное поведение:(Zsh обычно не разделяет текстовые данные с использованием IFS, если это явно не требуется, но обратите внимание, что здесь пустой аргумент неожиданно отсутствует в списке.)
источник
$@
не является особенным в этом отношении:$x
расширяется максимум до одного слова, но пустые переменные расширяются до нуля (не пустое слово) Попробуйтеprint -l a $foo b
сfoo
пустым или неопределенным.В одном из ответов говорится
$*
(что я называю «сплатом») редко бывает полезно.Я ищу в Google с
G() { IFS='+' ; w3m "https://encrypted.google.com/search?q=$*" ; }
Так как URL, часто делится с
+
, но моя клавиатура делаетлегче достичь , чем
+
,$*
+$IFS
чувствовать себя полезным.источник