Вопрос или проблема
это работает как ожидается в bash:
> t="ls -l"
> $t #== ls -l
> "$t" #== "ls -l"
ls -l: команда не найдена
Но в zsh я получил это:
> t="ls -l"
> $t #== "ls -l"
ls -l: команда не найдена
Как я могу заставить оболочку разобрать значение переменной снова, как это делает bash?
Если вы хотите, чтобы переменная расширялась в более чем один аргумент, используйте массив:
var=(ls -l)
$var
Но для хранения кода самым очевидным типом хранения является функция:
myfunction() ls -l
Или:
myfunction() ls -l "$@"
чтобы эта функция принимала дополнительные аргументы, которые передаются в ls
.
Тот факт, что bash
, как и большинство других оболочек, подобных Bourne, разделяет нецитированные переменные при расширении, на мой взгляд, является ошибкой. Смотрите какие проблемы это вызывает. Но если вы хотите такого поведения, вы можете установить опцию shwordsplit
. Вы также можете добавить опцию globsubst
, чтобы восстановить другую ошибку, найденную в bash
и других оболочках, подобных Bourne, где расширение переменной также подлежит глобализации (также известной как расширение путей). Или воспользуйтесь полным набором возможностей с emulate sh
или emulate ksh
(но потеряете несколько других функций zsh).
Не прибегая к этому, вы также можете сказать zsh
, чтобы явно разделить переменную:
var="ls -l"
$=var # разделить по $IFS, как $var в bash/sh
${(s[ ])var} # разделить только по пробелам независимо от значения $IFS
var="*.txt"
echo $~var # выполнить расширение имени файла, как $var в bash/sh
var="ls -ld -- *.txt"
$=~var # выполнить и разделение слов, и генерацию имен файлов
Этот пункт освещен в zsh faq.
Коротко говоря. Существуют несколько способов исправить это поведение:
- Установите
setopt shwordsplit
:
setopt shwordsplit t="ls -l" $t
- Используйте
eval
(что лучше избегать из-за возможных проблем с безопасностью):
t="ls -l" eval $t
Если переменная уже определена как строка, вы можете использовать
${=t}
Флаг =
говорит zsh использовать разделение слов при расширении переменной.
Ответ или решение
Вопрос, как вызвать скрипт с пробелами в переменной в оболочке Zsh, стал распространенной проблемой среди пользователей, особенно тех, кто привык к поведению Bash. Давайте рассмотрим, как можно достичь желаемого результата, а также объясним, почему Zsh обрабатывает переменные иначе.
Поведение Bash и Zsh
В Bash действительно можно выполнять команды, храня их в переменной, как показано в примере:
t="ls -l"
$t # Разворачивается как ls -l
Тем не менее, в Zsh такая конструкция может привести к ошибке, поскольку переменная не разбивается на аргументы так, как ожидалось.
Способы вызова скрипта с пробелами в Zsh
Для корректного выполнения команд, содержащих пробелы, в Zsh можно использовать несколько методов:
-
Использование массива:
Если требуется передать команду с несколькими аргументами, лучше использовать массивы:var=(ls -l) $var
Этот подход позволяет избежать путаницы с пробелами.
-
Определение функции:
Более элегантным способом хранения команд является создание функции:myfunction() { ls -l "$@" }
Эта функция может принимать дополнительные аргументы и передавать их команде
ls
. -
Использование
eval
(осторожно):
Хотя это возможно, использование командыeval
может привести к проблемам с безопасностью:t="ls -l" eval $t
Следует применять этот метод с осторожностью.
-
Использование
setopt shwordsplit
:
Вы можете включить опциюshwordsplit
, чтобы Zsh вел себя как Bash в отношении обработки пробелов:setopt shwordsplit t="ls -l" $t
-
Специальные синтаксисы в Zsh:
Чтобы явно разделять переменные, можно использовать ${=t}, который указывает Zsh на необходимость разбить строку:t="ls -l" ${=t}
Или:
var="ls -l" ${(s[ ])var}
Резюме
Существует множество способов вызвать команды с пробелами в переменных в Zsh. Наиболее безопасными и универсальными решениями являются использование массивов или создание функций. Использование eval
может быть рискованным и должно применяться только в доверительных сценариях.
Эти знания помогут вам адаптироваться к особенностям Zsh и использовать более эффективные и безопасные методы для работы с командами. Если у вас есть дополнительные вопросы по данной теме, не стесняйтесь задавать их!