zsh: substitute-if-undef-or-null для $1, $2 …: ${1:substitution} не работает.

Вопрос или проблема

Я хотел бы использовать стандартные параметры, т.е. мой минимально воспроизводимый пример:

#!/usr/bin/zsh
a=${1:a}
printf 'a: "%s"\n' "${a}"
b=${2:./build}
printf 'b: "%s"\n' "${b}"

Я ожидал увидеть, при запуске ./demo.zsh без аргументов:

a: "a"
b: "./build"

Вместо этого я получаю

a: ""
./demo.zsh:4: bad floating point constant
  • почему ${1:a} кажется предполагает, что $1 установлен и ненулевой, но развернутый $a является пустой строкой?
  • откуда возникает ошибка при парсинге с плавающей точкой? Если я заменяю ./build на build, появляется сообщение об ошибке: b является неизвестным модификатором.

Если я запускаю ./demo.zsh asdf bar, я получаю

a: "/tmp/fasf"
./demo.zsh:4: bad floating point constant

что, честно говоря, еще более сбивает с толку; откуда взялось /tmp/ (это текущий каталог).

Это основывается на комментарии от Stéphane.

В оболочке Zsh ${1:a} это то же самое, что и $1:a, что означает применение модификатора :a к $1. Модификатор :a документирован в zshexpn(1):

a      Превращает имя файла в абсолютный путь: добавляет текущий 
       каталог, если необходимо; удаляет сегменты пути `.`; и 
       удаляет сегменты пути `..` и сегменты, которые непосредственно
       предшествуют им.

Следовательно, ${1:a} будет пустой строкой, если $1 пуст или не установлен. Это будет путь, начинающийся с /tmp/fasf, если $1 это какая-то непустая строка, и ваш текущий каталог был /tmp/fasf.

:. не является допустимым модификатором в том же смысле, что и :a, но расширение ${1:n} будет допустимым для целого числа n и вернет конец строки в $1, начиная с позиции n. Это наследие от оболочек ksh88 и ksh93. Это означает, что так как . не является допустимым модификатором, оболочка пытается интерпретировать его как арифметическое выражение для целей вычисления подстроки. Вот почему вы получаете ошибку для ${2:./build}, строка ./build не может быть использована в качестве смещения.

В конечном итоге, вы хотели использовать одно из стандартных ${1-a} или ${1:-a} расширений для предоставления значения по умолчанию в случае, если $1 не установлен или пуст.

${1:a} Этот синтаксис неправильный для предоставления значений по умолчанию. Он пытается использовать a как модификатор, что не соответствует вашему намерению.

Сообщения об ошибках bad floating point constant и unknown modifier возникают из-за того, что zsh пытается интерпретировать a и ./build как модификаторы или специальный синтаксис, которыми они не являются.

Я воспроизвел скрипт с Bash, и он должен действительно работать так же в Zsh.

#!/bin/bash
a=${1:-a}
printf 'a: "%s"\n' "${a}"
b=${2:-./build}
printf 'b: "%s"\n' "${b}"

Вывод 1:

$:. ./test sss fff

a: "sss"
b: "fff"

Вывод 2:

$:. ./test  

a: "a"
b: "./build"

Синтаксис ${1:a} не является правильным способом предоставления значения по умолчанию для позиционного параметра.

Следует использовать ${1:-default}, чтобы предоставить значение по умолчанию, если параметр не установлен или пуст.

${1:-a} Это означает, что если $1 не установлен или пуст, используйте a в качестве значения по умолчанию.

${2:-./build} Это означает, что если $2 не установлен или пуст, используйте ./build в качестве значения по умолчанию.

.

Ответ или решение

Вопрос, предложенный вам для анализа, связан с неправильным использованием синтаксиса подстановки в оболочке Zsh и связанными ошибками. Zsh предлагает множество функций для управления переменными и их подстановкой, и понимание этих функций помогает избежать ошибок и непредвиденного поведения скриптов. Давайте разберем ситуацию подробно.

Теория (Theory)

Рассмотрим начальный пример кода:

#!/usr/bin/zsh
a=${1:a}
printf 'a: "%s"\n' "${a}"
b=${2:./build}
printf 'b: "%s"\n' "${b}"

Этот скрипт имеет цель задать стандартные параметры, если не переданы соответствующие аргументы. Оператор ${1:a} предполагает наличия файла и должен превратить относительный путь в абсолютный. Однако использование этого выражения является неправильным для замены значения по умолчанию параметра.

В Zsh модификатор :a используется для обработки имен файлов. Документация утверждает, что он превращает имя файла в абсолютный путь, добавляя текущий каталог, если это необходимо, и убирает сегменты пути . и ... Этот модификатор никак не связан с подстановкой значений по умолчанию.

Использование конструкции ${2:./build} также некорректно, так как символ : в данном контексте предполагает, что слева и справа от него должны присутствовать специальные модификаторы, чего здесь нет. Ошибка «bad floating point constant» указывает, что Zsh пытается интерпретировать символы после двоеточия как арифметическое выражение, ожидая числовое значение, а не строковое.

Для достижения ожидаемого результата следует использовать форму подстановки ${parameter:-default_value}, которая гарантированно возвращает заданное значение default_value, если parameter не определен или равен null.

Пример (Example)

Следующий исправленный пример демонстрирует правильный подход:

#!/usr/bin/zsh
a=${1:-a}
printf 'a: "%s"\n' "${a}"
b=${2:-./build}
printf 'b: "%s"\n' "${b}"

Используя ${1:-a} и ${2:-./build}, скрипт корректно возвращает a, когда $1 не установлен, и возвращает ./build, когда $2 не установлен. Здесь конструкция ${parameter:-word} обеспечивает подстановку word только в случае, если parameter не задан или равен пустой строке.

Применение (Application)

Применим вышеописанную теорию для разбора ошибок и их устранения в Zsh. При написании скриптов на Zsh важно учитывать:

  1. Различие в синтаксисе модификаторов и подстановки значений по умолчанию.
  2. Тщательная работа с параметрами командной строки и обеспечение их значений по умолчанию.
  3. Диагностика и устранение ошибок при помощи изучения справочной документации Zsh, такой как zshexpn.

Используя правильные конструкции, вы можете обеспечить надежную работу скриптов, которые обрабатывают аргументы командной строки с заданием значений по умолчанию. Применяя методики внимательного чтения документации и осознанного выбора синтаксиса, вы избежите множества типичных ошибок, которые могут затруднять отладку и сопровождение скриптов.

Таким образом, следуя подходу TEA, вы сможете минимизировать ошибки, использовать возможности языка Zsh на полную мощность, а также улучшить качество и долговечность ваших скриптов. В результате этого подхода ваш скрипт будет работать как в иных оболочках, так и в Zsh, при условии, что корректная версия оболочки заранее определена в заголовке скрипта (#!/usr/bin/zsh). Это критично для обеспечения интероперабельности в разных средах.

Заключение

В завершение важно подчеркнуть, что написание и сопровождение скриптов требует глубокого понимания синтаксиса и особенностей поведения системы. Хоть Zsh и обладает схожестью с другими оболочками, специфические расширения и конструкты могут вызывать недопонимания и ошибки, как продемонстрировано в данном случае. За счет правильного выбора конструкций параметров с заменой по умолчанию вы создаете основу для более надежных и понятных скриптов, что особенно важно в сложных ИТ-проектах.

Оцените материал
Добавить комментарий

Капча загружается...