Why bash indirect expansion has to use temp variable? - bash

From https://stackoverflow.com/a/10820494/1764881, I know that the standard way of doing it seems to be:
var="SAMPLE$i"
echo ${!var}
But, I can't seem to do any of these following forms. They all failed:
echo ${!SAMPLE$i}
echo ${!"SAMPLE$i"}
I read the bash man page, but I still couldn't understand. Is it true that the first form is the only form accepted?

Yes. The underlying logic is that all parameter expansions take a single, literal word as the name of the parameter to expand, and any additional operator does something to the result. ! is no exception; var is expanded as usual, but the result is expanded again.
(As an aside, even arrays follow this rule. It might seem that something like ${array[2]%foo} applies two operators to array, but really array[2] is treated as the name of a single parameter. There is a little difference, as the index is allowed to be an arbitrary arithmetic expression rather than a literal number.)
(And for completeness, I should mention the actual exceptions, ${!prefix*} and ${!name[*]}, which confusingly use the same operator ! for querying variables themselves. The first lists variable names starting with the same prefix; the second lists the keys of the named array.)

Related

Creating a string that calls a variable

I could use some suggestions, this probably isn't the most elegant way to do this but it's what came to mind. Let me add a couple snippets for clarity.
week1="01-\(04\|05\|06\|07\|08\|09\|10\)-2016"
week2="01-\(11\|12\|13\|14\|15\|16\|17\)-2016"
week3="01-\(18\|19\|20\|21\|22\|23\|24\)-2016"
week4="01-\(25\|26\|27\|28\|29\|30\|31\)-2016"
week5="02-\(01\|02\|03\|04\|05\|06\|07\)-2016"
I have a text file that has dates in each line, for example
293232343;01-02-2016;blah;more blah
234872348;02-01-2016;blah;extra blah
I am trying to create a loop so I can grab different data depending on the calendar week.
current_week=`date +%W`
for (( c=1; c<=$current_week; c++ ))
do
sort -k2 archive.txt|tr "\\t" ";"|grep $week$c|while read line; do
My problem is that $week$c doesn't actually translate to $week1, $week2, etc. Any easy solutions here? Thanks in advance.
From man bash:
If the first character of parameter is an exclamation point, a level
of
variable indirection is introduced. Bash uses the value of the vari-
able formed from the rest of parameter as the name of the variable;
this variable is then expanded and that value is used in the rest of
the substitution, rather than the value of parameter itself. This is
known as indirect expansion. The exceptions to this are the expansions
of ${!prefix*} and ${!name[#]} described below. The exclamation point
must immediately follow the left brace in order to introduce indirec-
tion.
Then, what you need is
var="week$c"
... grep "${!var}" ...

What's the meaning of `declare -i` in bash? Does it restrict variable to only numbers?

Can anyone explain purpose of declare -i in bash? Is it for security so that you don't do string operations on numbers?
If I assign a string-number to a variable, I can already do mathematical functions on it, so what's the point of declare -i?
declare -i sets the integer attribute for a name. It doesn't affect the use of the variable at all, only assignments to the variable. The right-hand side of an assignment is treated as if it were an arithmetic expression, so that foo=3+5, foo="3 + 5", and foo=$((3 + 5)) are identical.
In my opinion, it's not very useful, as it provides little benefit over an explicit arithmetic expression. You save 3 characters, "..." vs. $((...)), (unless you can safely leave the expression unquoted). Worse, it obscures the semantics of the assignment at the point it is actually made. (That is, when you seen an assignment, you need to look around to see if the integer attribute was set on the name before you know what the assignment actually does.)

Bash Script - Exclamation Point within Variable Reference

I'm looking over a script (which has been used successfully in the past) which contains the following:
node=1
while :
do
userKey=WEB_${node}_USER
userVal=`echo ${!userKey}`
I have not been able to figure out why an exclamation point would be added to a variable reference like this. What purpose does "!" serve in this context?
It's rare for me to do much scripting so if I am missing any details please let me know and I will try to provide more information. I have not been able to find this answer elsewhere.
Thanks in advance!
It's called indirect parameter expansion. Where $userKey expands to the value of the variable userKey, ${!userKey} expands to the value of the variable whose name is the value of userKey. Since usrKey has the value WEB_1_USER (given the current value of $node, ${!userKey} expands to the same result as $WEB_1_USER.
Its use is somewhat rare, since in many cases (including, it appears, here) an array WEB_USER of user names would be clearer than a set of numbered variables.
WEB_USER=(alice bob charlie)
node=1
while :
do
userVal=${WEB_USER[node]}
In bash, I thought that the only characters that retained their meta-character status inside double quotes were the dollar sign ($), the back-tick (`) and the backslash (\).

Bash variable concatenation

Which form is most efficient?
1)
v=''
v+='a'
v+='b'
v+='c'
2)
v2='a'` `'b'` `'c'
Assuming readability were exactly the same to you, and that's a stretch, would 1) mean creating and throwing away a few string immutables (like in Python) or act as a Java "StringBuffer" with periodical expansion of the buffer capacity? How are string concatenations handled internally in Bash?
If 2) were just as readable to you as 1), would the backticks spawn subshells and would that be more costly, even as a potential 'no-op' than what is done in 1) ?
Well, the simplest and most efficient mechanism would be option 0:
v="abc"
The first mechanism involves four assignments.
The second mechanism is bizarre (and is definitely not readable). It (nominally) runs an empty command in two sub-shells (the two ` ` parts) and concatenates the outputs (an empty string) with the three constants. If the shell simply executes the back-tick commands without noting that they're empty (and it's not unreasonable that it won't notice; it is a weird thing to try — I don't recall seeing it done in my previous 30 years of shell scripting), this is definitely vastly slower.
So, given only options (1) and (2), use option (1), but in general, use option (0) shown above.
Why would you be building up the string piecemeal like that? What's missing from your example that makes the original code sensible but the reduced code shown less sensible.
v=""
x=$(...)
v="$v$x"
y=$(...)
v="$v$y"
z=$(...)
v="$v$z"
This would make more sense, especially if you use each of $x, $y and $z later, and/or use intermediate values of $v (perhaps in the commands represented by triple dots). The concatenation notation used will work with any Bourne-shell derivative; the alternative += shell will work with fewer shells, but is probably slightly more efficient (with the emphasis on 'slightly').
The portable and straight forward method would be to use double quotes and curly brackets for variables:
VARA="beginning text ${VARB} middle text ${VARC}..."
you can even set default values for empty variables this way
VARA="${VARB:-default text} substring manipulation 1st 3 characters ${VARC:0:3}"
using the curly brackets prevents situations where there is a $VARa and you want to write ${VAR}a but end up getting the contents of ${VARa}

Difference b/w getting output to array in bash using `command` vs ($(command))

What is difference b/w these two command and output variable:
output=($(command))
And
output=`command`
In the first case, the resulting variable is an array, with the number of elements depending on the output of your command. In the second case, it is a scalar containing the complete result value. This difference is only due to the extra outer brackets in the first case.
Try it with, for example, the ls command to see the difference.
Additionally, the $(command) form has superseded the 'command' form for command substitution.
The only difference is that $() calls can be nested, AFAIR (okay, backticks can be nested too, but you have to escape all nested backticks, so $() provides nicer syntax).

Resources