Как «расширить» переменную bash (Включенный код работает для bash, но не для zsh)

Это была ошибка в некоторых старых версиях 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в качестве псевдонима для , в то время как].

0
23.05.2017, 15:40
2 ответа

Важное различие между bash и zsh заключается в способе run2 вызовов run , а именно в эффекте оставить $ 1 без цитирования.

  • В zsh, run $ 1 применяет оператор «remove-if-empty» к $ 1 , то есть вызывает run с первым аргументом, переданным в run2 , за исключением того, что если первый аргумент run2 был пуст (или если run2 был вызван без аргументов), то run будет называется без аргументов.
  • В других оболочках в стиле Борна, таких как bash, 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 .

3
28.01.2020, 02:27

Добро пожаловать в мир цитирования и цитирования сюрпризов.

Основная проблема заключается в том, что 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 в эмуляции.

Предполагается, что 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

Вторая строка отличается от второй строки для любой другой оболочки.

Рекомендуется прочитать ответы на этот вопрос

0
28.01.2020, 02:27

Теги

Похожие вопросы