Вопрос или проблема
Я вижу проблему с zsh, где символ глоба внутри переменной не разворачивается так, как я ожидал. Следующий пример лучше объясняет это.
$ echo $0
-bash
$ echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
$ file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% echo $0
zsh
Macbook% echo $HOME/Downloads/zsh-test/*
/Users/bruce/Downloads/zsh-test/file1 /Users/bruce/Downloads/zsh-test/file2 /Users/bruce/Downloads/zsh-test/file3 /Users/bruce/Downloads/zsh-test/file4
Macbook% file=*; echo $HOME/Downloads/zsh-test/$file
/Users/bruce/Downloads/zsh-test/*
Я ожидал, что последняя команда будет разворачиваться так же, как в bash. Есть идея, что я делаю не так?
Это будет впервые, когда я вижу, чтобы кто-то жаловался на это (чаще всего мы видим, как люди жалуются на то, что при расширении параметров не происходит разделения слов).
Большинство людей ожидает
echo $file
что будет выводить содержимое переменной $file
, и раздражаются, когда такие оболочки, как bash
, этого не делают (это поведение унаследовано от оболочки Bourne, к сожалению, не исправлено ksh и указано в POSIX для интерпретатора sh
), и это вызывает много ошибок и уязвимостей в безопасности, и поэтому вам нужно заключать все переменные в кавычки в этих оболочках.
Смотрите, например: Безопасностные последствия забытия заключить переменную в кавычки в bash/POSIX оболочках
Я вижу, что вы тоже этого ожидаете, так как пишете echo $0
, а не echo "$0"
.
zsh
это исправил. Он по умолчанию не делает ни глобирования, ни разделения слов при расширении параметров. Вам нужно запросить это явно:
echo $=file
: выполнить разделение словecho $~file
: выполнить глобированиеecho $=~file
: выполнить оба
Или вы можете включить опции globsubst
и shwordsplit
, чтобы получить такое же поведение, как в оболочках, подобных Bourne (эти две опции включены, когда zsh
запускается как sh
для совместимости с sh
), но я бы не рекомендовал это, если вам не нужно, чтобы zsh
интерпретировал код, написанный для другой оболочки (и даже в этом случае было бы более логично интерпретировать этот код в эмуляции sh
в локальном контексте с emulate -L sh
).
Здесь называть вашу переменную file
в
file=*
может ввести в заблуждение, если вы намерены, чтобы она была развернута при присваивании¹.
filename_pattern=*
будет более разумным. Если вы хотите переменную, содержащую имена всех ненастроенных файлов в текущем каталоге, вы можете сделать:
files=(*)
или:
files=( *(N) )
чтобы это присваивание не завершилось неудачей, если в текущем каталоге нет ненастройных файлов.
То есть используйте присваивание переменной массива. Это (file=(*)
) будет работать так же, как в bash
или ksh93
², mksh
или yash
, кроме того, что zsh
не имеет этой другой неудачи оболочки Bourne, при которой шаблон остается неразворачиваемым, когда нет совпадений.
¹ Обратите внимание, что *
является совершенно допустимым именем для файла в системе, подобной Unix. Я немного успокаиваюсь, зная, что rm -f -- $file
удаляет файл, имя которого хранится в $file
, даже если этот файл называется *
.
² ksh93 имеет files=( ~(N)* )
как эквивалент files=( *(N) )
zsh, хотя bash и yash, как zsh, также имеют опцию nullglob
, которую вы можете включить глобально, чтобы применить это ко всему расширению глобов (хотя это не то, что вам нужно в общем случае).
Ответ или решение
Вопрос о том, почему метасимволы в переменных в zsh не расширяются так, как в bash, имеет свои корни в различиях в философии и архитектуре этих двух оболочек. Знания о том, как zsh обрабатывает перменные и метасимволы, могут помочь разработчикам и системным администраторам более эффективно справляться с задачами автоматизации и скриптинга.
Сравнение Bash и Zsh
Расширение переменных и метасимволов
В bash при присвоении метасимвола переменной происходит неявное расширение этого метасимвола при его использовании. Рассмотрим следующий пример:
file=*
echo $HOME/Downloads/zsh-test/$file
В данном случае, при выполнении этой команды в bash, переменная $file
расширится до всех файлов в директории, что и ожидается. Однако, когда вы выполняете аналогичный код в zsh:
file=*
echo $HOME/Downloads/zsh-test/$file
Вы получите ; это происходит потому, что zsh, в отличие от bash, не выполняет автоматическую подстановку метасимволов при их использовании в переменных.
Специфика Zsh
Zsh имеет ряд функций, которые обеспечивают более предсказуемое управление переменными. Одной из таких функций является возможность управления расширением переменных через специальный синтаксис:
$=file
– выполняет разбиение строки по словам.$~file
– выполняет подстановку метасимволов.$=~file
– выполняет и разбиение, и подстановку метасимволов.
Это поведение может быть совершенно неочевидным для пользователей, привыкших к bash, и именно здесь может возникнуть недопонимание.
Рекомендации
Если вы хотите, чтобы метасимволы корректно обрабатывались в zsh, рассмотрите следующие варианты:
-
Использование специального синтаксиса:
Например:echo $~file
В этом случае переменная
$file
будет обработана как метасимвол, и вы получите ожидаемый результат. -
Объявление переменной как массив:
Использование массивов в zsh позволяет избежать путаницы с синонимами и метасимволами. Например:files=(*) echo "${files[@]}"
Это гарантирует, что вы будете работать с массивом файлов, который корректно расширяется.
-
Изменение настроек оболочки:
Вы можете активировать опции оболочки, такие какglobsubst
иshwordsplit
. Но стоит отметить, что это может привести к неожиданным результатам, если ваш скрипт рассчитан на поведение, характерное для других оболочек, таких как bash.
Заключение
Понимание различий в расширении переменных и метасимволов между bash и zsh поможет вам избежать распространенных ошибок. Zsh предлагает большую гибкость с помощью своего синтаксиса, что позволяет управлять поведением оболочки более детально. Учитывая эти аспекты, вы сможете использовать мощные возможности zsh более эффективно и избегать досадных недоразумений в процессе работы со скриптами.