Как я присваиваю переменную в Bash, имя которого расширено ($) от другой переменной?

У меня есть часть сценария как ниже.

notify() {
printf -v "old_notif" "%d" "$notif_$2"
now=$(date +%s)
fark=$((now - old_notif))
echo $old_notif
if [ -z $old_notif ]; then  

.....

x1=$(date +%s)
export "notif_$2=$x1"

Я звоню, уведомляют с 2 параметрами.

notify xyz klm

Я создаю динамическую переменную в функции с экспортом. И основной сценарий находится в while цикл. Мой вопрос состоит в том, как я могу использовать notif_$2 переменная в if проверить? Или как я могу присвоиться, это - десятичное содержание к другой переменной? В примере я попробовал printf это не присваивает содержание.

2
задан 29 June 2017 в 05:09

1 ответ

TL; DR: Один путь local tmp="notif_$2"; printf -v "old_notif" "%d" "${!tmp}".

Вы пытаетесь развернуть параметр (в этом случае, переменная), чье имя должно самостоятельно быть получено путем расширения другого параметра (в этом случае, позиционного параметра). Кроме того, позиционный параметр должен быть расширен, затем связан с текстом notif_, произвести текст, который должен затем самостоятельно быть расширен.

Bash позволяет Вам сделать это чисто с косвенным расширением или nameref.

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

Путь 1: косвенное расширение

Косвенное расширение является самым подобным, концептуально разговор, к тому, что Вы записали. Вместо printf -v команда, которую Вы имеете теперь, можно использовать эти две команды:

local tmp="notif_$2"
printf -v "old_notif" "%d" "${!tmp}"

local делает tmp параметр, локальный для функции оболочки. Если по некоторым причинам Вы не хотите это, просто опустите слово local. Параметр не должны называть tmp.

Косвенное расширение покрыто 3.5.3 Расширениями Параметра Shell в справочнике Bash.

Путь 2: Namerefs

Иначе должен использовать nameref. nameref относится к параметру. Вы создаете его из названия параметра, но когда-то созданный, это ведет себя, как будто случается так, что параметр, когда Вы читаете из или пишете в него.

Для использования nameref замените Ваш printf -v команда с:

local -n ref="notif_$2"
printf -v "old_notif" "%d" "$ref"

Передача -n опция к local или declare заставляет недавно представленный параметр быть nameref. Заметьте это, в printf команда, Вы будете использовать обычное расширение параметра ($ref) - весьма прямое расширение - потому что оболочка выполняет косвенность автоматически для nameref.

Вам действительно нужно local или declare здесь, потому что -n ref="notif_$2" отдельно была бы ошибка и ref="notif_$2" отдельно не сделал бы nameref. declare без -g опция в функции оболочки ведет себя как local, таким образом, можно использовать тот стиль, если Вы предпочитаете. Или, в необычном случае, где Вы хотели, чтобы nameref был применим после возвращенной функции, Вы могли использовать declare -g Благодаря G-человеку для указания на ошибку об этом в более ранней версии этого ответа.

(В зависимости от Ваших потребностей могло бы даже иметь смысл использовать nameref для больше, чем просто этих двух строк. Чтобы учиться, как, продолжает читать.)

Namerefs покрыты 3,4 Параметрами Shell в справочнике Bash.

Подробное объяснение: как косвенное расширение работает

Функции Shell наиболее легко продемонстрированы посредством интерактивного использования, но позиционных параметров (как 2, расширенный через $2) не имейте вполне того же значения вне функции оболочки, и local встроенный не работает во всей внешней стороне функция (Вы использовали бы declare вместо этого, или ничто).

Так, для запуска с упрощенного примера предположите, что Вы работаете в интерактивном режиме в Вашей оболочке, и Вы работали:

x=foo
export "notif_$x=1234"

После выполнения этого, 1234 хранится в параметре notif_foo. (Это также экспортирует тот параметр как переменную среды.) Мы видим это путем осмотра notif_foo:

echo "$notif_foo"

Это производит 1234.

Ваш сценарий походит на не знание, что находится в x параметр. (В Вашем случае Вы вместо этого не знаете то, что находится во втором позиционном параметре, переданном Вашей функции оболочки. Я скоро доберусь до этого.)

Но можно создать название параметра и поместить его в другой параметр:

y="notif_$x"

Теперь y содержит notif_foo, таким образом, можно использовать косвенное расширение на y, который похож на это:

"${!y}"

Это расширяется до 1234, так же, как если бы Вы использовали "$notif_foo". Но Вы не должны знать $x foo использовать его.

Например, это присвоится 1234 кому: old_notif:

old_notif="${!y}"

Если необходимо отформатировать содержание $notif_foo, можно сделать это, также. Например, можно использовать printf если Вам нужен он. Эта команда подобна printf управляйте в своем вопросе, и имеет эффект присвоения 1234 кому: old_notif:

printf -v old_notif '%d' "${!y}"

(Это также работает в Вашем исходном стиле заключения в кавычки, т.е. printf -v "old_notif" "%d" "${!y}" имеет тот же эффект.)

Конечно, это полагается y параметр соответственно присвоен сначала.

Для записи функции оболочки Вы замените $x с $2, и Вы, вероятно, захотите использовать local встроенный для предотвращения временной переменной - который я теперь назову tmp вместо y- от утечки из объема функции.

local tmp="notif_$2"
printf -v old_notif '%d' "${!tmp}"

Или, использование заключения в кавычки разрабатывает Вас используемый в вопросе:

local tmp="notif_$2"
printf -v "old_notif" "%d" "${!tmp}"

Подробное объяснение: как работают Namerefs

Для испытания namrefs в интерактивном режиме необходимо использовать declare -n вместо local -n (потому что local только работы - или необходимы - в теле функции оболочки).

Как прежде, предположите, что Вы работали:

x=foo
export "notif_$x=1234"

Таким образом $notif_foo расширяется до 1234. Можно создать nameref к параметру, названному результатом расширения "notif_$x":

declare -n y="notif_$x"

Теперь y относится к имени notif_foo, и обычное расширение параметра на y автоматически разыменует то имя, таким образом, расширяясь notif_foo параметр. Таким образом, это расширяется до 1234, так же, как если бы Вы использовали $notif_foo:

"$y"

Для записи функции оболочки Вы замените $x с $2, и я рекомендую использовать local вместо declare. Я предлагаю также использовать несколько более описательное имя, чем y; для короткой функции без других nameref объявлений, ref вероятно, соответственно ясно.

local -n ref="notif_$2"
print -v old_notif '%d' "$ref"

Или, с заключением в кавычки разрабатывают, Вы использовали:

local -n ref="notif_$2"
print -v "old_notif" "%d" "$ref"

Namerefs Мощны

nameref позволяет Вам сделать больше с упомянутым параметр, чем просто чтение от него. Можно также, например, записать в него:

x=foo
declare -n y="notif_$x"
y=1234

Вторая команда создает nameref к параметру, который еще не мог бы существовать. Это не проблема! Это будет создано, когда Вы сначала присвоите ему, даже если то присвоение через nameref.

Третья команда похожа на него, присваивается 1234 кому: y, но действительно это присваивает его notif_foo. Теперь оба $y и $notif_foo расширьтесь до 1234. $notif_foo расширяется до 1234 потому что это - значение, сохраненное в notif_foo; $y расширяется до 1234 потому что y nameref для notif_foo.

Предположим, что Вы хотите знать что y относится к, все же. Таким образом, предположите, что Вы хотите добраться notif_foo, вместо 1234, от y. Ну, Вы можете, потому что с nameref, косвенное расширение имеет противоположность своего обычного эффекта. Это расширяется до notif_foo:

"${!y}"

В Вашей функции Вы могли представить notif_$2 через nameref.

Это предлагает другой способ иметь дело с notif_$2 в Вашей функции: можно представить его через nameref и использовать nameref для каждого последующего доступа.

В настоящее время Вы имеете:

x1=$(date +%s)
export "notif_$2=$x1"

Как альтернатива, Вы могли использовать:

x1=$(date +%s)
local -n ref="notif_$2"
ref="$x1"
export "${!ref}=$ref"

Это более сложно настолько необходимое, тем не менее, с тех пор, по-видимому, Вы только создали x1 параметр, потому что export "notif_$2=$(date +%s)" твердо читать. ref="$(date +%s)" так же просто, тем не менее, таким образом, можно опустить x1= строка и запись:

local -n ref="notif_$2"
ref="$(date +%s)"
export "${!ref}=$ref"

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

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

  • Вместо того, чтобы иметь необходимость записать notif_$2, можно просто записать ref.
  • Это работает даже в сценариях где запись notif_$2 является несоответствующим. Таким образом, больше не необходимо сделать что-либо специальное (как Путь 1 и Путь 2 выше) для расширения параметра, имя которого является результатом расширения notif_$2. Просто запишите ref.
  • Это даже работает на запись - и создание и сбрасывание - параметр. (После того, как точка объявления, ref=text записи к и могут даже создать упомянутый параметр; unset ref сбрасывает упомянутый параметр.)

Если Вы собираетесь использовать nameref всюду по своей целой функции, можно хотеть думать о большем количестве понятного имени для нее, чем ref. (Лучшее имя будет, конечно, определено задачей, которую Вы пишете функции для выполнения.)

5
ответ дан 2 December 2019 в 01:55

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

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