Increment variable name results in command not found - bash

I am trying to increment a variable name based on the input and call the value after the loop is done.
for i in `seq 5`; do
var$i="number is $i"
done
echo $var2 $var5
Results in
./test.sh: line 4: var1=number is 1: command not found
./test.sh: line 4: var2=number is 2: command not found
./test.sh: line 4: var3=number is 3: command not found
./test.sh: line 4: var4=number is 4: command not found
./test.sh: line 4: var5=number is 5: command not found
There are 2 things I don't understand:
"var1=number is 1" is understood as a command.
var2 and var5 are actually generated but they are not displayed outside of the loop.

You cannot use variable names with a number in it like that: you really need to say:
var1=1
var2=2
var3=3
var4=4
var5=5
Another approach is the usage of arrays.
As far as increasing is concerned (this is not part of the question, but I give is anyways), you might use something like this:
Prompt> var1=1
Prompt> var2=$((var1+1))
Prompt> echo $var2
2
(Mind the double brackets for making calculations)

To achieve the outcome required, the use of arrays are needed and so:
#!/bin/bash
for i in $(seq 5)
do
var[$i]="number is $i"
done
for i in "${var[#]}"
do
echo "$i"
done
Set the index and values for the array var accordingly and then loop through the array and print the values.

"var1=number is 1" command not found because that's not a command. For example, you can write:
for i in `seq 5`; do
echo var$i="number is $i"
done
And the output would look like
var1=number is 1
var2=number is 2
var3=number is 3
var4=number is 4
var5=number is 5
The variables have not been generated, you can't just dynamically generate variables. Try to use arrays

Related

Bash: Loop through lines of a file and assign line to numbered variable names

I intend to read the lines of a short .txt file and assign each line to variables containing the line number in the variable name.
File example.txt looks like this:
Line A
Line B
When I run the following code:
i=1
while read line; do
eval line$i="$line"
echo $line
((i=i+1))
done < example.txt
What I would expect during execution is:
Line A
Line B
and afterwards being able to call
$ echo $line1
Line A
$ echo $line2
Line B
However, the code above results in the error:
-bash: A: command not found
Any ideas for a fix?
Quote-removal happens twice with eval. Your double-quotes are getting removed before eval even runs. I'm not even going to directly answer that part, because there are better ways to do this:
readarray line < example.txt # bash 4
echo "${line[0]}"
Or, to do exactly what you were doing, with a different variable for each line:
i=1
while read line$((i++)); do
:
done < example.txt
Also check out printf -v varname "%s" value for a better / safer way to assign by reference.
Check out the bash-completion code if you want to see some complicated call-by-reference bash shenanigans.
Addressing your comment: if you want to process lines as they come in, but still save previous lines, I'd go with this construct:
lines=()
while read l;do
lines+=( "$l" )
echo "my line is $l"
done < "$infile"
This way you don't have to jump through any syntactic hoops to access the current line (vs. having to declare a reference-variable to line$i, or something.)
Bash arrays are really handy, because you can access a single element by value, or you can do "${#lines[#]}" to get the line count. Beware that unset lines[4] leaves a gap, rather than renumbering lines[5:infinity]. See the "arrays" section in the bash man page. To find the part of the manual that documents $# expansion, and other stuff, search in the manual for ##. The Parameter Expansion section is the first hit for that in the bash 4.3 man page.
eval line$i="$line" tells bash to evaluate the string "line1=Line A", which attempts to invoke a command named A with the environment variable "line1" set to the value of Line. You probably want to do eval "line$i='$line'"

Passing variable name as param to function in bash

I am trying to read up 3 similar files with different names to different arrays. Because i didn't want to use unnecessary code i am trying to create functions that would accept array names as params, but i am getting error 'command not found'.
hello.sh file code:
#!/bin/bash
declare -a row_1
declare -a row_2
declare -a row_3
load_array()
{
ROW="$2"
let i=0
while read line; do
for word in $line; do
$ROW[$i]=$word
((++i))
done
done < $1
}
load_array $1 row_1
load_array $2 row_2
load_array $3 row_3
Calling this file from terminal with: sh hello.sh 1.txt 2.txt 3.txt
List of errors i am getting:
hello.sh: line 13: row_1[0]=9: command not found
hello.sh: line 13: row_1[1]=15: command not found
hello.sh: line 13: row_1[2]=13: command not found
hello.sh: line 13: row_2[0]=12: command not found
hello.sh: line 13: row_2[1]=67: command not found
hello.sh: line 13: row_2[2]=63: command not found
hello.sh: line 13: row_3[0]=75: command not found
hello.sh: line 13: row_3[1]=54: command not found
hello.sh: line 13: row_3[2]=23: command not found
In the assignment syntax, what is to the left of the equal sign must be either a variable name (when assigning to a scalar), or a variable name followed by a word in square brackets (when assigning to an array element). In your code, $ROW[$i]=$word doesn't match this syntax (there's a $ at the beginning, so it can't possibly be an assignment); it's just a word that happens to contain the character =.
In bash, you can use the declare builtin to assign to a variable whose name is the result of some computation such as a variable expansion. Note that unlike for a straight assignment, you do need double quotes around the value part to prevent word splitting and filename expansion on $word. Pass the option -g if you're doing the assignment in a function and you want the value to remain after the function returns.
declare -g $ROW[$i]="$word"
If you're running an old version of bash that doesn't have declare -g, you can use eval instead. This would be the method to use to assign to a dynamically-named variable in plain sh. Take care of quoting things properly: if ROW is e.g. row_1, then the string passed as the argument to eval must be row_1[$i]=$word (the shell command to parse and execute).
eval "$ROW[\$i]=\$word"
The ideal way to do this with modern (bash 4.3+) syntax is thus:
load_array() {
declare -n _load_array__row=$2
declare -a _load_array__line
_load_array__row=( )
while read -r -a _load_array__line; do
_load_array__row+=( "${_load_array__line[#]}" )
done <"$1"
}
(The variable names are odd to reduce the chance of collisions with the calling function; the other answers you're given will have trouble if asked to load content into a variable named ROW or line, for instance, as they'll be referring to local variables rather than global ones in describing their destinations).
A similar mechanism compatible with bash 3.2 (the ancient release shipped by Apple), avoiding the performance hit associated with inner loops and the bugs associated with glob expansion (see what your original code does to a line containing *!) follows:
load_array() {
local -a _load_array__array=( )
local -a _load_array__line
while read -r -a _load_array__line; do
_load_array__array+=( "${_load_array__line[#]}" )
done <"$1"
eval "$2=( \"\${_load_array__array[#]}\" )"
}

Use grep inside a if statement

I have to look if a file has the work England. I have used grep
This is what I have so far
#!/bin/bash
cd ~/Desktop/htmlFiles/
echo "Starting"
if `grep -inE "england" maps.html` ; then
echo "yaaaay"
fi
But the output is:
Starting
./trial.sh: line 4: 22:: command not found
Why doesn't it return yaaaay ?
#!/bin/bash
cd ~/Desktop/htmlFiles/
echo "Starting"
if grep -qinE "england" maps.html ; then
echo "yaaaay"
fi
Gave me the required output. Is that a good practise?
Backtick substitution will return the output of the command. In this case, you are outputting the line number with grep's output, following by the matching line. Hence, there appears to be something on line 22, but the shell tries to execute the command 22:, which then fails.
The variant you have posted as a followup answer will work, as grep returns 0 on a successful match, but 1 if the match fails.

Bash re-assignment of value to variable "command not found" [duplicate]

This question already has answers here:
Dynamic variable names in Bash
(19 answers)
Closed 8 years ago.
Any ideas why this is happening? Why do I have to manually explicitly reassign the variable but can't do it if I have another variable in the name of the variable?
SCRIPT:
#!/bin/bash
a_1=1
a_2=1
for temp in 1 2
do
a_$temp="2"
echo $((a_$temp))
done
a_1=2
a_2=2
echo $a_1
echo $a_2
OUTPUT:
[dgupta#della4 Rates_Of_Quenching]$ ./test.sh
./test.sh: line 8: a_1=2: command not found
1
./test.sh: line 8: a_2=2: command not found
1
2
2
Instead of:
a_$temp="2"
Use:
declare a_$temp="2"
to create variable with dynamic name.
As far as bash is concerned, you are trying to execute the command 'a_1=2', rather than perform an assignment. You can get around this by using declare, or its synonym typeset:
'a_1=2' # bash: a_1=2: command not found
typeset 'a_1=2'
echo $a_1 # 2
declare 'a_1=3'
echo $a_1 # 3
While it is possible to use declare, you might want to take advantage of bash arrays (which have been around since bash version 2) rather than using variables with numerical suffixes:
a=(1 1)
echo ${a[0]} # 1
echo ${a[1]} # 1
for i in 0 1; do a[i]=2; done
echo ${a[0]} # 2
echo ${a[1]} # 2

BASH scripting: n-th parameter of $# when the index is a variable?

I want to retrieve the n-th parameter of $# (the list of command line parameters passed to the script), where n is stored in a variable.
I tried ${$n}.
For example, I want to get the 2nd command line parameter of an invocation:
./my_script.sh alpha beta gamma
And the index should not be explicit but stored in a variable n.
Sourcecode:
n=2
echo ${$n}
I would expect the output to be "beta", but I get the error:
./my_script.sh: line 2: ${$n}: bad substitution
What am I doing wrong?
You can use variable indirection. It is independent of arrays, and works fine in your example:
n=2
echo "${!n}"
Edit: Variable Indirection can be used in a lot of situations. If there is a variable foobar, then the following two variable expansions produce the same result:
$foobar
name=foobar
${!name}
Try this:
#!/bin/bash
args=("$#")
echo ${args[1]}
okay replace the "1" with some $n or something...
The following works too:
#!/bin/bash
n=2
echo ${#:$n:1}
The portable (non-bash specific) solution is
$ set a b c d
$ n=2
$ eval echo \${$n}
b
eval can help you access the variable indirectly, which means evaluate the expression twice.
You can do like this eval alph=\$$n; echo $alph

Resources