bash for loop running more than the length of array - bash

#!/bin/bash
arrck=
arrrlt=
if true; then
arrck+=("1")
arrrlt+=("one")
else
echo "something"
fi
for i in ${!arrck[*]}
do
echo "${arrck[i]} is ${arrrlt[i]}"
done
output 1--
is
1 is one
output 2--tracing on
./Building_block.sh
+ arrck=
+ arrrlt=
+ true
+ arrck+=("1")
+ arrrlt+=("one")
+ for i in '${!arrck[*]}'
+ echo ' is '
is
+ for i in '${!arrck[*]}'
+ echo '1 is one'
1 is one
Why is the loop running with no value assigned to i ? i can say it tried to execute echo "${arrck[]} is ${arrrlt[]}" and picking the blank space as value.

When you assign a value to a variable you are actually assigning a value to cell/position 0 in an array, eg:
$ x=5
$ echo ${x}
5
$ echo ${!x[#]}
0
$ echo ${x[0]}
5
$ echo ${x[#]}
5
In your example the first set of commands assign the empty string to index position '0' of the 2 arrays arrck[] and arrrlt[]:
$ arrck=
$ arrrlt=
$ echo "${!arrck[#]} : ${!arrrlt[#]}"
0 : 0
The if/then/else block then appends a second set of values to your arrays with indexes 1 and values of 1 and one.
The for loop then loops through the 2 available indices for the arrck[] array, namely 0 and 1.
What you probably want to do is start out by dropping/deallocating your arrays (as opposed to creating cell 0), eg:
$ unset arrck
$ unset arrrlt
$ echo "${!arrck[#]:-undefined} : ${!arrrlt[#]:-undefined}"
undefined : undefined

Related

Shell: why does while loop processing additional char

This is my simple shell script
root#Ubuntu:/tmp# cat -n script.sh
1 echo
2 while x= read -n 1 char
3 do
4 echo -e "Original value = $char"
5 echo -e "Plus one = `expr $char + 1`\n"
6 done < number.txt
7 echo
root#Ubuntu:/tmp#
And this is the content of number.txt
root#Ubuntu:/tmp# cat number.txt
12345
root#Ubuntu:/tmp#
As you can see on the code, I'm trying to read each number and process it separately. In this case, I would like to add one to each of them and print it on a new line.
root#Ubuntu:/tmp# ./script.sh
Original value = 1
Plus one = 2
Original value = 2
Plus one = 3
Original value = 3
Plus one = 4
Original value = 4
Plus one = 5
Original value = 5
Plus one = 6
Original value =
Plus one = 1
root#Ubuntu:/tmp#
Everything looks fine except for the last line. I've only have 5 numbers, however it seems like the code is processing additional one.
Original value =
Plus one = 1
Question is how does this happen and how to fix it?
It seems the input file number.txt contains a complete line, which is terminated by a line feed character (LF). (You can verify the input file is longer than 5 using ls -l.) read eventually encounters the LF and gives you an empty char (stripping the terminating LF from the input as it would without the -n option). This will give you expr + 1 resulting in 1. You can explicitely test for the empty char and terminate the while loop using the test -n for non-zero length strings:
echo "12345" | while read -n 1 char && [ -n "$char" ]; do echo "$char" ; done

I stored values in Multi Dimensional Array. When printing the values, it's printing the second iteration values and first iteration is being replaced

This block of code is looping through a file and loading each word into a multi dimensional array.
lcv=0
declare -A db
while read line;
do
lcv1=0
echo $line
for i in $line;
do
db[$lcv,$lcv1]=$i
echo $lcv,$lcv1,${db[$lcv,$lcv1]};
#echo ${db[$lcv]}
((++lcv1))
done
((++lcv))
done < data.txt # File Contains records of 4 fields.
echo ${db[0,1]}
echo ${db[0,0]}
Little pseudo 2D array using bash
I just re-use your algorithm, whiping all echo and useless steps.
#!/bin/bash
unset x y db
y=0
declare -A db
while read line ;do
for i in $line ;do
db[$((x++)),$y]=$i
done
((y++))
x=0
done <<<$'0 1 2 3\n4 5 6 7\n8 9 a b\nc d e f'
Now if you
declare -p db x y
bash will print:
declare -A db='([0,0]="0" [0,1]="4" [0,2]="8" [0,3]="c" [3,3]="f" [3,2]="b" [3,1]="7" [3,0]="3" [2,2]="a" [2,3]="e" [2,0]="2" [2,1]="6" [1,1]="5" [1,0]="1" [1,3]="d" [1,2]="9" )'
declare -- x="0"
declare -- y="4"
At this point, I just wanna purpose to change 9th line: ((y++)) by ((y++,maxx=maxx>x?maxx:x)). This will populate maxx (to 4 in this sample)
Then inverting the array:
for i in {0..4};do # this syntax is nice, but don't support variables
for((j=0;j<y;j++)){ # this syntaxe could use variables
echo -n ${db[$i,$j]}\
}
echo
done
will print:
0 4 8 c
1 5 9 d
2 6 a e
3 7 b f
If the data.txt contains this:
$ cat data.txt
l0val0 l0val1 l0val2 l0val3
l1val0 l1val1 l1val2 l1val3
l2val0 l2val1 l2val2 l2val3
l3val0 l3val1 l3val2 l3val3
l4val0 l4val1 l4val2 l4val3
l5val0 l5val1 l5val2 l5val3
Your program produce this:
$ ./script
l0val0 l0val1 l0val2 l0val3
0,0,l0val0
0,1,l0val1
0,2,l0val2
0,3,l0val3
l1val0 l1val1 l1val2 l1val3
1,0,l1val0
1,1,l1val1
1,2,l1val2
1,3,l1val3
l2val0 l2val1 l2val2 l2val3
2,0,l2val0
2,1,l2val1
2,2,l2val2
2,3,l2val3
l3val0 l3val1 l3val2 l3val3
3,0,l3val0
3,1,l3val1
3,2,l3val2
3,3,l3val3
l4val0 l4val1 l4val2 l4val3
4,0,l4val0
4,1,l4val1
4,2,l4val2
4,3,l4val3
l5val0 l5val1 l5val2 l5val3
5,0,l5val0
5,1,l5val1
5,2,l5val2
5,3,l5val3
l0val1
l0val0
That goes to show that the value of $lcv selects each row (line), and the value of $lcv1 selects each word (record) divided on spaces or tabs.
It is working correctly from what I can see.
If we add this lines at the end of the script:
echo "end of first script"
for i in {0..5}; do
for j in {0..3}; do
printf 'db[%s,%s]=%s ' "$i" "$j" "${db[$i,$j]}"
done
echo
done
echo
declare -p db
We will get this output:
end of first script
db[0,0]=l0val0 db[0,1]=l0val1 db[0,2]=l0val2 db[0,3]=l0val3
db[1,0]=l1val0 db[1,1]=l1val1 db[1,2]=l1val2 db[1,3]=l1val3
db[2,0]=l2val0 db[2,1]=l2val1 db[2,2]=l2val2 db[2,3]=l2val3
db[3,0]=l3val0 db[3,1]=l3val1 db[3,2]=l3val2 db[3,3]=l3val3
db[4,0]=l4val0 db[4,1]=l4val1 db[4,2]=l4val2 db[4,3]=l4val3
db[5,0]=l5val0 db[5,1]=l5val1 db[5,2]=l5val2 db[5,3]=l5val3
declare -A db=([1,1]="l1val1" [1,0]="l1val0" [1,3]="l1val3" [1,2]="l1val2" [0,0]="l0val0" [0,1]="l0val1" [0,2]="l0val2" [0,3]="l0val3" [5,1]="l5val1" [5,0]="l5val0" [5,3]="l5val3" [5,2]="l5val2" [3,3]="l3val3" [3,2]="l3val2" [3,1]="l3val1" [3,0]="l3val0" [2,2]="l2val2" [2,3]="l2val3" [2,0]="l2val0" [2,1]="l2val1" [4,0]="l4val0" [4,1]="l4val1" [4,2]="l4val2" [4,3]="l4val3" )
Now, the question is: What do you think that is wrong?.

variable in for loop does not give proper output in shell script

sh version : 1.14.7
#!/bin/sh
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
i=0
for i in 1 2 3 4
do
echo "value of the $i th varible is $cpu_to_eth$i"
done
it is not working properly,the output should be
value of the 1 th varible is 10
value of the 2 th varible is 20
value of the 3 th varible is 30
value of the 4 th varible is 40
Without requiring bash:
#!/bin/sh
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
for i in 1 2 3 4; do
eval echo "value of the $i th varible is "$"cpu_to_eth$i"
done
This should work in any POSIX shell (e.g. in dash, the default shell in Ubuntu).
The point is that you need two evaluations (for an indirect evaluation):
evaluate $i to get the name of the variable (cpu_to_eth$i)
evaluate the variable cpu_to_eth$i to get its actual value
The second-order evaluation needs a separate eval (or a bash-ism)
With bash, it's more appropriate to use an array here, rather than have multiple variables.
Example of an array:
cpu_to_eth_arr=( 10 20 30 40 )
for i in "${cpu_to_eth_arr[#]}"
do
echo "$i"
done
Another way, using an associative array:
cpu_to_eth[1]=10
cpu_to_eth[2]=20
cpu_to_eth[3]=30
cpu_to_eth[4]=40
for i in "${!cpu_to_eth[#]}"
do
echo "value of the $i th varible is ${cpu_to_eth[$i]}"
done
Using bash you can perform the Shell parameter expansion:
#!/bin/bash
cpu_to_eth1=10
cpu_to_eth2=20
cpu_to_eth3=30
cpu_to_eth4=40
i=0
for i in 1 2 3 4
do
val=cpu_to_eth${i} # prepare the variable
echo value of the $i th varible is ${!val} # expand it
done

Bash - Different Value of parameter Inside If condition

input from file $2 : 1 -> 2
while read -a line; do
if (( ${line[2]} > linesNumber )); then
echo "Graph does not match known sites4"
exit
fi
done < "$2"
For some reason inside the if condition, the value of ${line[2]) is not 2
but if I print the value outside if:
echo `${line[2]}`
2
What's linesNumber? Even if you put $linesNumber, where is it coming from?
If you are tracking the line number, you need to set it and increment it. Here's my sample program and data. It's inspired by your example, but doesn't do exactly what you want. However, it shows you how to setup a variable that tracks the line number, how to increment it, and how to use it in an if statement:
foo.txt:
this 1
that 2
foo 4
barf 4
flux 5
The Program:
lineNum=0
while read -a line
do
((lineNum++))
if (( ${line[1]} > $lineNum ))
then
echo "Line Number Too High!"
fi
echo "Verb = ${line[0]} Number = ${line[1]}"
done < foo.txt
Output:
Verb = this Number = 1
Verb = that Number = 2
Line Number Too High!
Verb = foo Number = 4
Verb = barf Number = 4
Verb = flux Number = 5

Shell Script: How do I add the digits of a number?

I am making a shell script that takes a single number (length is unimportant) from the command line and adds the digits of it together. I thought I had it, but it won't work and either displays "0+3+4+5" if the command input is 345 or it displays the variables when I use expr to add them.
#!/bin/bash
sum=0
i="$(expr length $1)"
s=$1
for i in $(seq 0 $((${#s} - 1))); do
value=${s:$i:1}
typeset -i value
sum=$sum+$value
done
echo $sum
Also doesn't work when I replace it with sum='expr $sum + $value'
any ideas?
What you are looking for is sum=$(($sum+$value)).
#!/bin/bash
expr $(echo $1| sed 's/./& + /g;s/..$//')
For example, if the argument is 12345, this translates it to the string 1 + 2 + 3 + 4 + 5 and uses expr to evaluate it.

Resources