Skip to main content
Bounty Awarded with 50 reputation awarded by CommunityBot
more example cases, fixed grammar, some minor rewording
Source Link
LL3
  • 5.6k
  • 9
  • 20

I’ll have amy go at trying and explain this, but forgive me if I won’t follow the example you provided. I’ll rather try to guide you along my own, different, pathapproach.

You say you already understand concepts such as “variables” and “expanding them”, etc. so I’ll skipjust skim over some background knowledge that would otherwise be requiredrequire deeper focus.

The answer lies in the fact that the above implicit way to declare a variable (foo="bar"), it.. implicitly.. will makemakes Bash consider that variable of being of the type that is most commonly used in the typical usage scenario for a shell (BTW: Bash is a shell application, not a true programming language though it may resemble one in several aspects).

Such type is the string type, i.e. a sequence of characters with no particular meaning. Therefore a string is what you get when you use the implicit declaration.

But you, as the programmer, sometimes need to rather consider a variable as, e.g., a number.. on which you need to do arithmetic operations.. and by using just an implicit declaration like foo=5+6, Bash won’t make Bash assign value 11 to foo as you might expect. It will rather assign to the variablefoo the sequence of the three characters 5 + 6.

So, justJust say:

and Bash will happily do the math for you, and assign the numeric value 11 to variable foo.

Declaring numbers (precisely integers, because Bash still does not understand decimals, floating points, and all that) is probablymay be the first reason offor using declare, but it’s not the only reason. As you have already understood, there are a few other attributes you can give to variables. For instance you can have Bash to always make a variable’s value uppercase no matter what: if you say declare -u foo, then from then on when you say foo=bar Bash actually assigns the string BAR to the variable foo.

In order to give any of these attributes to a variable, you must use the declare command. There’s, there’s no other choice.

 

Now, one other of the attributes you can give through declare is the infamous “name-ref” one, the -n attribute. (And herenow I'm going to reconnect to resume the concept I put on hold earlier).

The name-ref attribute, basically, allows Bash programmers to usefor another way to change the value of a variable. It more precisely gives an indirect way to do that.

Here’s howhow it works:

You first have to declare a variable having the -n attribute, and it is very recommended (though not strictly required, but it makes things simpler) that you also give a value to this very variable on the same declare command. Like this:

Which means that, from then on, you can say something like baz=10+3 to make foo get the value of 13. Assuming of course that foo was previously declared as integer (declare -i) like we did just one minute ago, otherwise it’ll get the sequence of the four characters 1 0 + 3.

Try that.

Also: if you change foo’s value directly, as in foo=15, you will see 15 also by saying echo $“${baz}. This is because variable baz declared as name-ref of foo always reflects foo’s value.

In other words: theThe above declare -n command is said a “name-reference” because it makes variable baz refer to the name of another variable, and by doing so you. In fact we have declared indirectly refer to that other variable’sbaz has having value "foo" which, because of the -n option, is handled by Bash as the name for another variable.

Now, whywhy on Earth would you ever want to do that ?

In general thisfact so advanced that when a programmer faces a problem that would really require a name-ref, it is neededalso likely that such problem should rather be addressed to by using a proper programming language instead of Bash.

One of those advanced needs is, for instance, when you, as the programmer, cannot know during development which variable you will have to use in a specific point of a script, but it will be fully known dynamically at run-time. ButAnd given that there’s no way for any programmer to intervene at run-time, is there. So the only wayoption is to make provision beforehand for such situation in the script, and a “name-ref” can often be the only viable way. ImagineAs a widely known use case of this advanced need, think of plug-ins, for instance,. The programmer of a “plugin-able” program needs to make generic provision for future (and possibly third-party) plug-ins beforehand. Therefore the programmer will need to use facilities like a name-ref in Bash.

One other advanced need is when you have to deal with huge amount of data in RAM and you also need to pass that data around functions of your script that also have to modify that data along the variableway. In such case you wantcould certainly copy that data from one function to useanother (like Bash does when you do dest_var="${src_var}" or when you invoke functions like in myfunc "${src_var}"), but being that data a specific pointhuge amount it would make for a huge waste of the script comes as dynamicRAM inputand for a very inefficient operation. So The Solution if such situations arise is to use not a copy of the scriptdata, instead of beingbut a hardwiredreference to that data. In Bash, a name-ref. This use case is really the norm in any modern programming language, but it is quite exceptional when it comes to Bash, because Bash is mostly designed for short simple scripts that mostly deal with files and external commands and thus Bash scripts seldom have to pass huge amount of data between functions. And when a script’s code as one mostfunctions do need to share some data (access it and modify it) this is usually doesachieved by just using a global variable, which is quite the norm in Bash scripts as much as it is very deprecated in proper programming languages.

I know it canThen, there may be hard to understand this at firsta notable use case for name-refs in Bash, but it’ll struckand (maybe ironically) it is associated to when you use yet other types of variables:

  1. variables that are declared as “indexed arrays” (declare -a)
  2. variables that are declared as “associative arrays” (declare -A).

These are a type of variables that may be more easily (as well as obvious the first time you’ll actuallymore efficiently) passed along functions by using name-refs instead of by normal copying, even when they don’t carry huge amounts of data.

If all these examples sound weird, and still incomprehensible, it’s only because name-refs are indeed an advanced topic, and a rare need it for a practical problemthe typical usage scenario of Bash.

I could tell you about a few occasions on which I for one hadhave found use for name-refs in Bash, but so far they werehave been mostly for advancedquite “esoteric” and complicated needs, and I’m afraid they won’t helpthat if I described them I would only complicate things for you understand at this point of your learning. Just to mention the least complex (and possibly not esoteric): returning values from functions. This feature isBash does not really present in Bashsupport this functionality, and thusso I obtained the same by using name-refs. This, incidentally, is exactly what your example code does.


Besides this, a small personal advice, which would actually be better suited for a comment but I haven't been able to condense it enough to fit into StackExchange's comment's limits.

I think that the most you should do at the moment is to just experiment with name-refs by using the simple examples I showed and maybe with the example code you provided, disregarding for the moment the “why on earth” part and focusing only on the “how it works” part. By experimenting a bit, the “how” part may sink better into your mind, so that the “why” part will come clear to you in due time when (or if) you’ll have a real practical problem for which a name-ref would truly come in handy.

I’ll have a go at trying and explain this, but forgive me if I won’t follow the example you provided. I’ll rather try to guide you along my own, different, path.

You say you already understand concepts such as “variables” and “expanding them”, etc. so I’ll skip over some background knowledge that would otherwise be required.

The answer lies in the fact that the above implicit way to declare a variable (foo="bar"), it.. implicitly.. will make Bash consider that variable of being of the type that is most commonly used in the typical usage scenario for a shell (BTW: Bash is a shell application, not a true programming language though it may resemble one in several aspects).

Such type is the string type, i.e. a sequence of characters with no particular meaning.

But you, as the programmer, sometimes need to consider a variable as, e.g., a number.. on which you need to do arithmetic operations.. and by using just an implicit declaration like foo=5+6, Bash won’t assign value 11 to foo as you might expect. It will rather assign to the variable the sequence of the three characters 5 + 6.

So, just say:

and Bash will happily do the math for you and assign the numeric value 11 to variable foo.

Declaring numbers (precisely integers, because Bash still does not understand decimals, floating points, and all that) is probably the first reason of using declare, but it’s not the only reason. As you have already understood, there are a few other attributes you can give to variables. For instance you can have Bash to always make a variable’s value uppercase no matter what: if you say declare -u foo, from then on when you say foo=bar Bash actually assigns the string BAR to the variable foo.

In order to give any of these attributes to a variable, you must use the declare command. There’s no other choice.

Now, one other of the attributes you can give through declare is the infamous “name-ref” one, the -n attribute. (And here I'm going to reconnect to the concept I put on hold earlier).

The name-ref attribute, basically, allows Bash programmers to use another way to change the value of a variable. It more precisely gives an indirect way to do that.

Here’s how it works:

You first have to declare a variable having the -n attribute, and it is very recommended (though not strictly required, but it makes things simpler) that you also give a value to this very variable on the same declare command. Like this:

Which means that you can say something like baz=10+3 to make foo get the value of 13. Assuming of course that foo was previously declared as integer (declare -i) like we did just one minute ago, otherwise it’ll get the sequence of the four characters 1 0 + 3.

Try that.

Also: if you change foo’s value directly, as in foo=15, you will see 15 also by saying echo ${baz}. This is because variable baz declared as name-ref of foo always reflects foo’s value.

In other words: the above declare command is said a “name-reference” because it makes variable baz refer to the name of another variable, and by doing so you indirectly refer to that other variable’s value.

Now, why on Earth would you ever want to do that ?

In general this is needed when you, as the programmer, cannot know during development which variable you will have to use in a specific point of a script, but it will be fully known dynamically at run-time. But there’s no way for any programmer to intervene at run-time, is there. So the only way is to make provision beforehand for such situation in the script, and a “name-ref” can often be the only viable way. Imagine, for instance, that the variable you want to use in a specific point of the script comes as dynamic input of the script, instead of being hardwired in script’s code as one most usually does.

I know it can be hard to understand this at first, but it’ll struck you as obvious the first time you’ll actually need it for a practical problem.

I could tell you about a few occasions on which I for one had found use for name-refs, but they were for advanced needs and I’m afraid they won’t help you understand at this point of your learning. Just to mention the least complex: returning values from functions. This feature is not really present in Bash, and thus I obtained the same by using name-refs.

I’ll have my go at trying and explain this, but forgive me if I won’t follow the example you provided. I’ll rather try to guide you along my own, different, approach.

You say you already understand concepts such as “variables” and “expanding them”, etc. so I’ll just skim over some background knowledge that would otherwise require deeper focus.

The answer lies in the fact that the above implicit way to declare a variable (foo="bar"), it.. implicitly.. makes Bash consider that variable of being of the type that is most commonly used in the typical usage scenario for a shell.

Such type is the string type, i.e. a sequence of characters with no particular meaning. Therefore a string is what you get when you use the implicit declaration.

But you, as the programmer, sometimes need to rather consider a variable as, e.g., a number.. on which you need to do arithmetic operations.. and using an implicit declaration like foo=5+6 won’t make Bash assign value 11 to foo as you might expect. It will rather assign to foo the sequence of the three characters 5 + 6.

Just say:

and Bash will happily do the math for you, and assign the numeric value 11 to variable foo.

Declaring numbers (precisely integers, because Bash still does not understand decimals, floating points, and all that) may be the first reason for using declare, but it’s not the only reason. As you have already understood, there are a few other attributes you can give to variables. For instance you can have Bash to always make a variable’s value uppercase no matter what: if you say declare -u foo, then from then on when you say foo=bar Bash actually assigns the string BAR to the variable foo.

In order to give any of these attributes to a variable, you must use the declare command, there’s no other choice.

 

Now, one other of the attributes you can give through declare is the infamous “name-ref” one, the -n attribute. (And now I'm going to resume the concept I put on hold earlier).

The name-ref attribute, basically, allows Bash programmers for another way to change the value of a variable. It more precisely gives an indirect way to do that.

Here’s how it works:

You declare a variable having the -n attribute, and it is very recommended (though not strictly required, but it makes things simpler) that you also give a value to this very variable on the same declare command. Like this:

Which means that, from then on, you can say something like baz=10+3 to make foo get the value of 13. Assuming of course that foo was previously declared as integer (declare -i) like we did just one minute ago, otherwise it’ll get the sequence of the four characters 1 0 + 3.

Also: if you change foo’s value directly, as in foo=15, you will see 15 also by saying echo “${baz}. This is because variable baz declared as name-ref of foo always reflects foo’s value.

The above declare -n command is said a “name-reference” because it makes variable baz refer to the name of another variable. In fact we have declared baz has having value "foo" which, because of the -n option, is handled by Bash as the name for another variable.

Now, why on Earth would you ever want to do that ?

In fact so advanced that when a programmer faces a problem that would really require a name-ref, it is also likely that such problem should rather be addressed to by using a proper programming language instead of Bash.

One of those advanced needs is, for instance, when you, as the programmer, cannot know during development which variable you will have to use in a specific point of a script, but it will be fully known dynamically at run-time. And given that there’s no way for any programmer to intervene at run-time, the only option is to make provision beforehand for such situation in the script, and a “name-ref” can be the only viable way. As a widely known use case of this advanced need, think of plug-ins, for instance. The programmer of a “plugin-able” program needs to make generic provision for future (and possibly third-party) plug-ins beforehand. Therefore the programmer will need to use facilities like a name-ref in Bash.

One other advanced need is when you have to deal with huge amount of data in RAM and you also need to pass that data around functions of your script that also have to modify that data along the way. In such case you could certainly copy that data from one function to another (like Bash does when you do dest_var="${src_var}" or when you invoke functions like in myfunc "${src_var}"), but being that data a huge amount it would make for a huge waste of RAM and for a very inefficient operation. So The Solution if such situations arise is to use not a copy of the data, but a reference to that data. In Bash, a name-ref. This use case is really the norm in any modern programming language, but it is quite exceptional when it comes to Bash, because Bash is mostly designed for short simple scripts that mostly deal with files and external commands and thus Bash scripts seldom have to pass huge amount of data between functions. And when a script’s functions do need to share some data (access it and modify it) this is usually achieved by just using a global variable, which is quite the norm in Bash scripts as much as it is very deprecated in proper programming languages.

Then, there may be a notable use case for name-refs in Bash, and (maybe ironically) it is associated to when you use yet other types of variables:

  1. variables that are declared as “indexed arrays” (declare -a)
  2. variables that are declared as “associative arrays” (declare -A).

These are a type of variables that may be more easily (as well as more efficiently) passed along functions by using name-refs instead of by normal copying, even when they don’t carry huge amounts of data.

If all these examples sound weird, and still incomprehensible, it’s only because name-refs are indeed an advanced topic, and a rare need for the typical usage scenario of Bash.

I could tell you about occasions on which I for one have found use for name-refs in Bash, but so far they have been mostly for quite “esoteric” and complicated needs, and I’m afraid that if I described them I would only complicate things for you at this point of your learning. Just to mention the least complex (and possibly not esoteric): returning values from functions. Bash does not really support this functionality, so I obtained the same by using name-refs. This, incidentally, is exactly what your example code does.


Besides this, a small personal advice, which would actually be better suited for a comment but I haven't been able to condense it enough to fit into StackExchange's comment's limits.

I think that the most you should do at the moment is to just experiment with name-refs by using the simple examples I showed and maybe with the example code you provided, disregarding for the moment the “why on earth” part and focusing only on the “how it works” part. By experimenting a bit, the “how” part may sink better into your mind, so that the “why” part will come clear to you in due time when (or if) you’ll have a real practical problem for which a name-ref would truly come in handy.

Source Link
LL3
  • 5.6k
  • 9
  • 20

I’ll have a go at trying and explain this, but forgive me if I won’t follow the example you provided. I’ll rather try to guide you along my own, different, path.

You say you already understand concepts such as “variables” and “expanding them”, etc. so I’ll skip over some background knowledge that would otherwise be required.

So I’ll start by saying that, at its most basic level, the declare command is just a way for you to tell Bash that you need a variable value (i.e. a value the might change during script execution), and that you will refer to that value using a specific name, precisely the name you indicate next to the declare command itself.

That is:

declare foo="bar"

tells Bash that you want the variable named foo have the value bar.

But.. hold on a minute.. we can do that without using declare at all, can’t we. As in:

foo="bar"

Very true.

Well, it so happens that the above simple assignment is actually an implicit way for.. in fact.. declaring a variable.

(It also so happens that the above is one of a few ways to change the value of the variable named foo; indeed it is precisely the most direct, concise, evident, straight-forward way.. but it’s not the only one.. .. I’ll come back to this later..).

But then, if it is so well possible to declare a “name that will tag variable values” (just “variable” from hereafter, for the sake of brevity) without using declare at all, why would you ever want to use this pompous “declare” command ?

The answer lies in the fact that the above implicit way to declare a variable (foo="bar"), it.. implicitly.. will make Bash consider that variable of being of the type that is most commonly used in the typical usage scenario for a shell (BTW: Bash is a shell application, not a true programming language though it may resemble one in several aspects).

Such type is the string type, i.e. a sequence of characters with no particular meaning.

But you, as the programmer, sometimes need to consider a variable as, e.g., a number.. on which you need to do arithmetic operations.. and by using just an implicit declaration like foo=5+6, Bash won’t assign value 11 to foo as you might expect. It will rather assign to the variable the sequence of the three characters 5 + 6.

So.. you need a way to tell Bash that you want foo to be considered a number, not a string.. and that’s what an explicit declare comes useful for.

So, just say:

declare -i foo=5+6  # <<- note the '-i' option: it means 'integer'

and Bash will happily do the math for you and assign the numeric value 11 to variable foo.

That is: by saying declare -i foo you give to variable foo the attribute of being an integer number.

Declaring numbers (precisely integers, because Bash still does not understand decimals, floating points, and all that) is probably the first reason of using declare, but it’s not the only reason. As you have already understood, there are a few other attributes you can give to variables. For instance you can have Bash to always make a variable’s value uppercase no matter what: if you say declare -u foo, from then on when you say foo=bar Bash actually assigns the string BAR to the variable foo.

In order to give any of these attributes to a variable, you must use the declare command. There’s no other choice.

Now, one other of the attributes you can give through declare is the infamous “name-ref” one, the -n attribute. (And here I'm going to reconnect to the concept I put on hold earlier).

The name-ref attribute, basically, allows Bash programmers to use another way to change the value of a variable. It more precisely gives an indirect way to do that.

Here’s how it works:

You first have to declare a variable having the -n attribute, and it is very recommended (though not strictly required, but it makes things simpler) that you also give a value to this very variable on the same declare command. Like this:

declare -n baz="foo"

This tells Bash that, from then on, each time you will use, or change, the value of the variable named baz, it shall actually use, or change, the value of the variable named foo.

Which means that you can say something like baz=10+3 to make foo get the value of 13. Assuming of course that foo was previously declared as integer (declare -i) like we did just one minute ago, otherwise it’ll get the sequence of the four characters 1 0 + 3.

Try that.

Also: if you change foo’s value directly, as in foo=15, you will see 15 also by saying echo ${baz}. This is because variable baz declared as name-ref of foo always reflects foo’s value.

In other words: the above declare command is said a “name-reference” because it makes variable baz refer to the name of another variable, and by doing so you indirectly refer to that other variable’s value.

Now, why on Earth would you ever want to do that ?

Well.. it’s worth saying that this is a feature for quite advanced needs.

In general this is needed when you, as the programmer, cannot know during development which variable you will have to use in a specific point of a script, but it will be fully known dynamically at run-time. But there’s no way for any programmer to intervene at run-time, is there. So the only way is to make provision beforehand for such situation in the script, and a “name-ref” can often be the only viable way. Imagine, for instance, that the variable you want to use in a specific point of the script comes as dynamic input of the script, instead of being hardwired in script’s code as one most usually does.

I know it can be hard to understand this at first, but it’ll struck you as obvious the first time you’ll actually need it for a practical problem.

I could tell you about a few occasions on which I for one had found use for name-refs, but they were for advanced needs and I’m afraid they won’t help you understand at this point of your learning. Just to mention the least complex: returning values from functions. This feature is not really present in Bash, and thus I obtained the same by using name-refs.