I have a part of script as below.
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"
I call notify with 2 parameters.
notify xyz klm
I create a dynamic variable in function with export. And the main script is in the while
loop. My question is how can I use notif_$2
variable in if
check? Or how can I assign it's decimal content to another variable? In the example I tried with printf
it doesn't assign the content.
TL;DR: One way is local tmp="notif_$2"; printf -v "old_notif" "%d" "${!tmp}"
.
You are trying to expand a parameter (in this case, a variable) whose name must itself be obtained by expanding another parameter (in this case, a positional parameter). Furthermore, the positional parameter must be expanded, then concatenated with the text notif_
, to produce the text that must then itself be expanded.
Bash lets you do this cleanly with indirect expansion or a nameref.
The first two sections of this post show drop-in replacements for your printf -v
command, using those techniques. The remaining sections are optional; they further explain these features and what you can do with them.
Indirect expansion is the most similar, conceptually speaking, to what you have written. Instead of the printf -v
command you have now, you can use these two commands:
local tmp="notif_$2"
printf -v "old_notif" "%d" "${!tmp}"
local
makes the tmp
parameter local to the shell function. If for some reason you don't want that, just omit the word local
. The parameter doesn't need to be called tmp
.
Indirect expansion is covered in 3.5.3 Shell Parameter Expansion in the Bash reference manual.
Another way is to use a nameref. A nameref refers to a parameter. You create it from the name of the parameter, but once created, it behaves as though it is that parameter when you read from or write to it.
To use a nameref, replace your printf -v
command with:
local -n ref="notif_$2"
printf -v "old_notif" "%d" "$ref"
Passing the -n
option to local
or declare
causes the newly introduced parameter to be a nameref. Notice that, in the printf
command, you will use ordinary parameter expansion ($ref
) -- not indirect expansion -- because the shell performs the indirection automatically for the nameref.
You do need local
or declare
here, because -n ref="notif_$2"
by itself would be an error and ref="notif_$2"
by itself wouldn't make a nameref. declare
without the -g
option in a shell function behaves like local
, so you can use that style if you prefer. Or, in the unusual case where you wanted the nameref to be usable after the function returned, you could use declare -g
Thanks to G-Man for pointing out a mistake about this in an earlier version of this answer.
(Depending on your needs, it might even make sense to use a nameref for more than just these two lines. To learn how, read on.)
Namerefs are covered in 3.4 Shell Parameters in the Bash reference manual.
Shell features are most easily demonstrated through interactive use, but positional parameters (like 2
, expanded via $2
) don't have quite the same meaning outside a shell function, and the local
builtin doesn't work at all outside a function (you would use declare
instead, or nothing).
So, to start with a simplified example, suppose you are working interactively in your shell and you have run:
x=foo
export "notif_$x=1234"
After you run that, 1234
is stored in the parameter notif_foo
. (It also exports that parameter as an environment variable.) We can see this by inspecting notif_foo
:
echo "$notif_foo"
This outputs 1234
.
Your scenario is analogous to not knowing what is in the x
parameter. (In your case, you instead don't know what's in the second positional parameter passed to your shell function. I'll get to that soon.)
But you can build the parameter name and put it in another parameter:
y="notif_$x"
Now y
holds notif_foo
, so you can use indirect expansion on y
, which looks like this:
"${!y}"
That expands to 1234
, just as if you had used "$notif_foo"
. But you don't need to know $x
is foo
to use it.
For example, this will assign 1234
to old_notif
:
old_notif="${!y}"
If you need to format the contents of $notif_foo
, you can do that, too. For example, you can use printf
if you need it. This command is similar to the printf
command in your question, and has the effect of assigning 1234
to old_notif
:
printf -v old_notif '%d' "${!y}"
(It also works in your original quoting style, i.e., printf -v "old_notif" "%d" "${!y}"
has the same effect.)
Of course, this relies on the y
parameter being suitably assigned first.
To write your shell function, you will replace $x
with $2
, and you will probably want to use the local
builtin to prevent your temporary variable--which I will now call tmp
instead of y
--from leaking out of the function's scope.
local tmp="notif_$2"
printf -v old_notif '%d' "${!tmp}"
Or, using the quoting style you used in the question:
local tmp="notif_$2"
printf -v "old_notif" "%d" "${!tmp}"
To try out namrefs interactively, you must use declare -n
rather than local -n
(because local
only works--or is needed--inside the body of a shell function).
As before, suppose you have run:
x=foo
export "notif_$x=1234"
Thus $notif_foo
expands to 1234
. You can create a nameref to the parameter named by the result of expanding "notif_$x"
:
declare -n y="notif_$x"
Now y
refers to the name notif_foo
, and ordinary parameter expansion on y
will automatically dereference that name, thereby expanding the notif_foo
parameter. That is, this expands to 1234
, just as if you had used $notif_foo
:
"$y"
To write your shell function, you will replace $x
with $2
, and I recommend using local
instead of declare
. I suggest also using a somewhat more descriptive name than y
; for a short function with no other nameref declarations, ref
is probably adequately clear.
local -n ref="notif_$2"
print -v old_notif '%d' "$ref"
Or, with the quoting style you've been using:
local -n ref="notif_$2"
print -v "old_notif" "%d" "$ref"
A nameref lets you do more with its referred-to parameter than just read from it. You can also, for example, write to it:
x=foo
declare -n y="notif_$x"
y=1234
The second command creates a nameref to a parameter that might not yet exist. That's no problem! It will be created when you first assign to it, even if that assignment is through the nameref.
The third command looks like it assigns 1234
to y
, but really it assigns it to notif_foo
. Now both $y
and $notif_foo
expand to 1234
. $notif_foo
expands to 1234
because that's the value stored in notif_foo
; $y
expands to 1234
because y
is a nameref for notif_foo
.
Suppose you want to know what y
refers to, though. That is, suppose you want to get notif_foo
, rather than 1234
, from y
. Well, you can, because with a nameref, indirect expansion has the opposite of its usual effect. This expands to notif_foo
:
"${!y}"
notif_$2
through a nameref.This suggests another way to deal with notif_$2
in your function: you can introduce it through a nameref, and use the nameref for each subsequent access.
Currently you have:
x1=$(date +%s)
export "notif_$2=$x1"
As an alternative, you could use:
x1=$(date +%s)
local -n ref="notif_$2"
ref="$x1"
export "${!ref}=$ref"
That's more complicated that necessary, though, since presumably you only created the x1
parameter because export "notif_$2=$(date +%s)"
is hard to read. ref="$(date +%s)"
is just as simple, though, so you can omit the x1=
line and write:
local -n ref="notif_$2"
ref="$(date +%s)"
export "${!ref}=$ref"
It occurs to me that you might have been using export
just to assign the parameter for use in your shell. If you don't actually need to export it to child processes, then you can just use the first two lines, and that's simpler than what you have.
If you do need to export it, use all three. It's still a bit more complicated than what you have... but it may let you simplify the rest of your function, because, afterwards:
notif_$2
, you can just write ref
.notif_$2
is inadequate. That is, it is no longer necessary to do anything special (like Way 1 and Way 2 above) to expand the parameter whose name is the result of expanding notif_$2
. Just write ref
.ref=text
writes to and can even create the referred-to parameter; unset ref
unsets the referred-to parameter.)If you're going to use a nameref throughout your whole function, you may want to think of a more meaningful name for it than ref
. (The best name will, of course, be determined by the task you are writing the function to carry out.)
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments