Сценарий оболочки: неверная замена индексов аргументов

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

Мне нужно выполнить расчеты, чтобы знать, какой аргумент мне нужно использовать. Как только он у меня появится, я смогу его вызвать, как здесь (упрощенная, общая версия):

    1 i=1
    2 k=$(( i+1 ))
    3 echo $i
    4 echo $k
    5 echo "${!i}"
    6 echo "${!k}"

Как и следовало ожидать:

    $ ./testo A B 
    1
    2
    A
    B

Во-первых, я нашел это выражение с восклицательным знаком где-то в Интернете и был рад этому, потому что оно работает. Однако я не понимаю, что он делает. На мой взгляд, ! обычно означает not, но здесь это точно работает. Без, однако, это не так. Я хотел бы узнать, почему.

Теперь я хотел упростить код и удалить, как мне кажется, ненужную вторую строку, заменив k в последней строке арифметическим выражением, как в строке 2. К моему удивлению, ни одна из моих попыток скопировать выражение для k из второй строки на самом деле не сработала:

    1 i=1
    2 k=$(( i+1 ))
    3 echo $i
    4 echo $k
    5 echo "${!i}"
    6 echo "${!k}"
    7 echo "${!$(( i+1 ))}"
    8 echo "${!(( i+1 ))}"
    9 echo "${!(( $i+1 ))}"

, как можно увидеть здесь:

    ./testo: line 7: ${!$(( i+1 ))}: bad substitution
    ./testo: line 8: ${!(( i+1 ))}: bad substitution
    ./testo: line 9: ${!(( $i+1 ))}: bad substitution

Что я делаю не так; какая будет хорошая замена; почему я не могу просто скопировать выражение для k из строки 2 на место k в строке 6?

Прочтите Расширение параметров оболочки. Примерно на середине страницы он попадает на ${!prefix*}.

SiKing 20.08.2024 19:41

Я действительно видел это, читал вдоль и поперек, но не мог понять. Как i или k можно приравнять к prefix* или около того? В этом документе есть множество примеров, но ничего по этой теме. В любом случае спасибо

udippel 20.08.2024 20:33

Помимо моего ответа ниже, я также отредактировал ваши теги. Я удалил оболочку и добавил bash, так как это применимо только к оболочке bash. Если вы работаете в любой другой оболочке, мой ответ ниже неверен, и вам нужно будет указать (с помощью правильного тега), на какую оболочку вы ориентируетесь.

SiKing 20.08.2024 21:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Из официальной документации (пункт 4):

Если первым символом параметра является восклицательный знак (!), а параметр не является ссылкой на имя, это вводит уровень косвенности. Bash использует значение, полученное путем расширения оставшейся части параметра, в качестве нового параметра; Затем оно расширяется, и это значение используется в остальной части расширения, а не в расширении исходного параметра. Это известно как косвенное расширение.

Итак, в вашем примере "${!i}", где i имеет значение 1, после косвенного расширения становится "$1". $1 — это первый аргумент, передаваемый в ваш пример сценария, A в вашем случае. Вот что выдает эхо.

Другие ваши примеры "${!$(( i+1 ))}" и «${!(( i+1 ))}" с использованием косвенного расширения становятся бессмысленными. $(( i+1 )) и (( i+1 )) являются недопустимыми именами параметров, поэтому вы получаете неверную ошибку замены. Обратите внимание, что «вещь» после ! снова не оценивается, он рассматривается как имя параметра буквально.

Ответ принят как подходящий

Расширения не являются макросами. Для расширения вам нужна переменная. Вы не можете его оптимизировать. Все в порядке, представленный код идеален.

Я не понимаю, что это делает

Косвенное расширение описано в руководстве по bash. См. Что такое непрямое расширение? Что означает ${!var*}? и https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html .

и удалите, как мне кажется, ненужную вторую строку, заменив k в последней строке арифметическим выражением, как в строке 2

Нет возможности.

Что я делаю не так

Ничего.

какая будет хорошая замена

k=$(( i+1 )); echo "${!k}" идеальная замена

почему я не могу просто скопировать выражение для k из строки 2 на место k в строке 6?

Расширения не являются макросами. ${! должен расширить переменную. Вы не можете объединять расширения в цепочку, они просто не анализируются и не реализуются таким образом.

Другие вопросы по теме