我有如下的一部分脚本。
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
它没有分配内容。
答案1
总结:一种方法是local tmp="notif_$2"; printf -v "old_notif" "%d" "${!tmp}"
。
您正在尝试扩展范围(在本例中为变量),其名称本身必须通过扩展另一个参数(在本例中为位置参数)来获取。此外,必须扩展位置参数,然后将其与文本连接起来notif_
,以生成必须扩展的文本。
Bash 可以让你干净利落地完成这个间接扩张或名称引用。
本文的前两部分展示了使用这些技术对命令的直接替换printf -v
。其余部分是可选的;它们进一步解释了这些功能以及您可以使用它们做什么。
方式一:间接扩张
间接扩张从概念上讲,与您所写的内容最为相似。除了printf -v
现在的命令,您还可以使用以下两个命令:
local tmp="notif_$2"
printf -v "old_notif" "%d" "${!tmp}"
local
使tmp
参数成为 shell 函数的本地参数。如果出于某种原因您不想这样,只需省略单词local
。该参数不需要被称为tmp
。
间接扩展涵盖3.5.3 Shell参数扩展在里面Bash 参考手册。
方法 2:Namerefs
另一种方法是使用一个名称引用。nameref 引用一个参数。你可以通过参数的名称来创建它,但是一旦创建,当你读取或写入它时,它的行为就像是那个参数一样。
要使用 nameref,请将printf -v
命令替换为:
local -n ref="notif_$2"
printf -v "old_notif" "%d" "$ref"
将选项传递-n
给local
或declare
会导致新引入的参数成为 nameref。请注意,在printf
命令中,您将使用普通参数扩展 ( $ref
) — — 而不是间接扩展 — — 因为 shell 会自动为 nameref 执行间接操作。
这里确实需要local
或,因为单独使用会出错,而且单独使用不会产生 nameref。在 shell 函数中,没有选项的行为类似于,因此,如果您愿意,可以使用该样式。或者,在特殊情况下,如果您希望 nameref 在函数返回后可用,则可以使用declare
-n ref="notif_$2"
ref="notif_$2"
declare
-g
local
declare -g
谢谢G-Man为了指出这个错误在此答案的早期版本中。
(根据您的需要,使用 nameref 甚至可能不仅仅用于这两行。要了解如何操作,请继续阅读。)
Nameref 涵盖在3.4 Shell 参数在里面Bash 参考手册。
详细说明:间接扩展的工作原理
Shell 特性最容易通过交互式使用来演示,但是位置参数(比如2
,通过 扩展$2
)在 shell 函数之外的含义并不完全相同,而local
内置函数在函数之外根本不起作用(你可以使用declare
它,或者什么都不用)。
因此,从一个简单的例子开始,假设你在 shell 中以交互方式工作,并且已经运行:
x=foo
export "notif_$x=1234"
运行该程序后,1234
它将存储在参数中notif_foo
。(它还将该参数导出为环境变量。)我们可以通过检查来看到这一点notif_foo
:
echo "$notif_foo"
这将输出1234
。
你的情况类似于不知道什么在x
参数中。(在您的例子中,您不知道传递给 shell 函数的第二个位置参数中的内容。我很快就会讲到。)
但是您可以构建参数名称并将其放在另一个参数中:
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
首先适当分配的参数。
要编写自己的 shell 函数,您需要$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
仅在 shell 函数体内有效或需要)。
与前面一样,假设您已经运行:
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"
要编写 shell 函数,您将$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"
Nameref 非常强大
nameref 可以让你对其引用的参数执行更多操作,而不仅仅是读取它。例如,你还可以写入它:
x=foo
declare -n y="notif_$x"
y=1234
第二条命令为可能尚不存在的参数创建 nameref。没问题!它将在您第一次赋值时创建,即使该赋值是通过名称引用。
第三条命令看起来就像它分配1234
给y
,但实际上它将其分配给notif_foo
。现在$y
和都$notif_foo
扩展为1234
。$notif_foo
扩展为1234
因为这是存储在 中的值notif_foo
;$y
扩展为1234
因为y
是 的名称引用notif_foo
。
假设你想知道什么y
引用了。也就是说,假设你想从 中得到notif_foo
,而不是。好吧,你可以,因为使用 nameref ,间接扩展会产生与其通常效果相反的效果。这将扩展为:1234
y
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
只是用来分配 shell 中使用的参数。如果您实际上不需要将其导出到子进程,那么您只需使用前两行,这比您拥有的更简单。
如果你做需要导出,请使用所有三个。这仍然比你拥有的要复杂一些……但它可能会让你简化其余的部分你的功能,因为之后:
- 您无需书写
notif_$2
,只需书写即可ref
。 - 这种方法在书写不充分的情况下也能奏效
notif_$2
。也就是说,不再需要做任何特殊的事情(如上面的方法 1 和方法 2)来扩展名称为扩展 的结果的参数notif_$2
。只需写入 即可ref
。 - 这甚至适用于写入——以及创建和取消设置——参数。(在声明点之后,写入甚至可以创建引用的参数;取消设置引用的参数。)
ref=text
unset ref
如果要在整个函数中使用 nameref,则可能需要为其想一个比 更有意义的名称ref
。 (当然,最好的名称将由您编写函数执行的任务决定。)