Это была ошибка в некоторых старых версиях zsh
, исправленная в 5.1. Список изменений:
15 июля 2015 г. Бартон Э. Шефер
- 35799: Src/params.c: с NO_EXEC, нижний индекс параметра разбора выражения для правильного баланса закрывающих фигурных скобок, но не выполнить подписку
после того, как об этом сообщилив тот же день.
В этих версиях ее можно воспроизвести с помощью:
$ zsh -nc '${a[1]}'
zsh:1: bad substitution
Согласно git bisect
, ошибка была введена в 2011 году в исправлении аналогичной проблемы для ассоциативных массивовв commit dfc26195c916d54163a3f0dd2eb159db2d974569, начиная с версии zsh-4.3.12
В более общем смысле zsh -n
(или anyshell -n
) весьма ограничен в своих возможностях анализировать код, поскольку, поскольку он не выполняет код, он мало что может сделать в областях, где способ оценки некоторого кода зависит от некоторого кода, который был запущен заранее.
Например, ${a[1+]}
недействителен для массива, но подходит для хеша. Не зная какой zsh -n
просто не жалуется.
Он также может давать ложные срабатывания, например:
alias aslongas=while
aslongas whatever; do
something
done
Где он жалуется на это неожиданное действие
, потому что он не признал aslongas
в качестве псевдонима для , в то время как
].
Важное различие между bash и zsh заключается в способе run2
вызовов run
, а именно в эффекте оставить $ 1
без цитирования.
run $ 1
применяет оператор «remove-if-empty» к $ 1
, то есть вызывает run
с первым аргументом, переданным в run2
, за исключением того, что если первый аргумент run2
был пуст (или если run2
был вызван без аргументов), то run
будет называется без аргументов. run $ 1
применяет оператор «split + glob» к $ 1
, то есть разбивает первый аргумент на run2
на фрагменты, разделенные пробелами¹, интерпретирует каждую часть как шаблон подстановки² и заменяет каждый шаблон подстановки, который соответствует одному или нескольким файлам, списком совпадений. Таким образом, run2 'ab' c
вызывает run
с аргументом ab
в zsh (аргумент передается без изменений), но вызывает run
с двумя аргументами a
и b
в bash (разделены на части, разделенные пробелами).
Мне нужен способ передать первый аргумент, который представляет собой строку с пробелами, и добавить этот стиль
$ @
в другую команду. напримерrun2 "a b" c
должен выдать1: a 2: b 3:
Ваше описание и ваш пример говорят разные вещи. Если вы хотите передать первый аргумент другой команде, используйте , запустите «$ 1»
, чтобы убедиться, что аргумент не разделен. Передача аргументов без изменений - это весь смысл "$ @"
.
Похоже, что вы действительно хотите разбить первый аргумент run2
на фрагменты, разделенные пробелами. В bash это можно сделать, отключив подстановочные знаки, а затем (предполагая, что IFS
не изменен по умолчанию), используя расширение без кавычек.
run2 () ( set -f; run $1; )
( echo "$ (somecommand)"
по сути эквивалентно запуску somecommand
в подоболочке, и похоже, что это то, что вы имели в виду, а не echo $ ( somecommand)
, который применяет split + glob к выходным данным команды, поэтому я удалил избыточную подстановку команды echo.)
В zsh вы можете использовать символ =
в параметре подстановка для выполнения разделения мира (без подстановки) для значения.
run2 () { run $=1; }
Синтаксис zsh несовместим с синтаксисом простого sh. Если вы хотите получить сценарий sh из zsh, вы можете использовать построение emulate
:
emulate sh -c '. myscript.sh'
Используйте emulate ksh
для имитации (некоторых функций) ksh. Это не эмулирует все функции bash, но позволяет использовать массивы.
¹ В целом, на основе значения IFS
.
² Если это не было отключено с помощью set -f
.
Добро пожаловать в мир цитирования и цитирования сюрпризов.
Основная проблема заключается в том, что zsh
по умолчанию не разбивается на символы IFS.
В этом: он отличается от всех других оболочек.
Чтобы проверить, как цитирование меняет способ работы оболочки, нам нужен код, который проверяет как цитируемые, так и некотируемые версии вашего кода.
Ваш код (добавление пары переменных):
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@" ; }
run1() { echo "`runa $1 $2 $3 `"; }
run1 'a b' c
Позвольте мне остановиться на деталях.
Предположим, вы создали локальную ссылку sh
, указывающую на место, где находится zsh
:
ln -s "/usr/bin/zsh" ./sh
А также предположим, что вы скопировали следующий сценарий в , поэтому
файл.
Сценарий, повторяющий каждую функцию с переменными в кавычках и без кавычек:
program() { printf '%02d-1: %6s 2: %6s 3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program $@ ; }
run1() { echo "`runa "$1" "$2" "$3"`"; }
run2() { echo "`runb "$1" "$2" "$3"`"; }
run3() { echo "`runa $1 $2 $3`"; }
run4() { echo "`runb $1 $2 $3`"; }
for i in `seq 4`; do
run"$i" 'a b' c
done
Затем, при выполнении, мы получаем следующее:
# Any shell (except zsh) results.
01-1: a b 2: c 3:
02-1: a 2: b 3: c
03-1: a 2: b 3: c
04-1: a 2: b 3: c
Только первый прогон (run1), где все заключено в кавычки, сохраняет 'a b '
присоединился.
Однако zsh действует так, как будто все цитируются все время:
# ZSH results.
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a b 2: c 3:
04-1: a b 2: c 3:
Предполагается, что zsh
будет эмулировать старые оболочки, если они будут называться sh
или ksh
.
Но на практике это не всегда верно:
$ ./sh ./so # emulated sh
01-1: a b 2: c 3:
02-1: a b 2: c 3:
03-1: a 2: b 3: c
04-1: a 2: b 3: c
Вторая строка отличается от второй строки для любой другой оболочки.
Рекомендуется прочитать ответы на этот вопрос