giving an array sum values in a for loop - arrays

Im having this annoying problem which not even my teacher can solve:/.
I want to fill an array with sum values from 1 to 100 this is my code:
while [ $i -le 100 ]
do
#filling the list with the sums of i at the pos i
sumList[$i]=$(echo $i | sum)
echo $i |sum
echo $sumList[$i]
i=$(($i+1))
done
And for some reason it just fills all spots with the first value (00034 1)
I have no Idea what to do

Here's ShellCheck:
Line 6:
echo $sumList[$i]
^-- SC1087: Use braces when expanding arrays, e.g. ${array[idx]} (or ${var}[.. to quiet).
^-- SC2128: Expanding an array without an index only gives the first element.
And with this fixed:
i=1
while [ $i -le 100 ]
do
#filling the list with the sums of i at the pos i
sumList[$i]=$(echo $i | sum)
echo $i |sum
echo ${sumList[$i]}
i=$(($i+1))
done
You get all the different checksums and block counts that you'd expect:
32802 1
32802 1
00035 1
00035 1
32803 1
32803 1
00036 1
00036 1
32804 1
32804 1
[...]

If you actually examine the output from that script (removing the echo $i |sum line), it should become evident what's happening:
00034 1[0]
00034 1[1]
00034 1[2]
: : :
00034 1[100]
As you can see, the line echo $sumList[$i] is giving you $sumList (which is identical to ${sumList[0]})and $i separately and that's because, as per the bash documentation (my emphasis):
Any element of an array may be referenced using ${name[subscript]}. The braces are required to avoid conflicts ...
So, if you change that to the correct ${sumList[$i]}`, you'll see that you are indeed setting up the array correctly, you just weren't printing it correctly:
00034 1
32802 1
00035 1
: : :
08244 1
And, for what it's worth, there are other facilities in bash that will make your code more succinct, if that is your goal:
for i in {0..100}; do sumList[$i]="$(echo $i | sum)" ; done
IFS=$'\n' ; echo "${sumList[*]}"

Related

Populate array in a for loop

I have an array of strings to pass through a script. The script is well-behaved, and will return error code 0 if the string "passes" and non-zero if it "fails." If the string passes, it should be included in a final array to be output or written to file or etc.
The problem I'm having is that the only item ending up in my final array is the first "passing" string.
#!/bin/bash
# validator.sh
if [[ $1 -le 10 ]]; then
exit 0
else
exit 1
fi
#!/bin/bash
# main.sh
numbers=(2 4 6 8 10 12 14 16)
keep=()
for n in ${numbers[#]}; do
if ./validator.sh $n; then
keep+=("$n")
fi
done
echo $keep
Running main.sh produces:
$ ./main.sh
2
but I expect 2 4 6 8 10
Unless you meant keep to be an array of matching elements, change:
keep+=("$n")
to
keep="$keep $n"
That would work with any Bourne compatible shell and is therefore better, if you're looking for BASH specific solution, the below will also work:
keep+="${n} "
If you DO want it to be an array, then in order to output all elements, you can use:
echo ${keep[#]}
As noted by #Jetchisel and #kamilCuk in the comments.
Since you wrote you want to output all elements or save them to a file, I had assumed you don't actually need an array here but perhaps you plan to use this data in other ways later:)

Append elements to an array in bash

I tried the += operator to append an array in bash but do not know why it did not work
#!/bin/bash
i=0
args=()
while [ $i -lt 5 ]; do
args+=("${i}")
echo "${args}"
let i=i+1
done
expected results
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
actual results
0
0
0
0
0
It did work, but you're only echoing the first element of the array. Use this instead:
echo "${args[#]}"
Bash's syntax for arrays is confusing. Use ${args[#]} to get all the elements of the array. Using ${args} is equivalent to ${args[0]}, which gets the first element (at index 0).
See ShellCheck: Expanding an array without an index only gives the first element.
Also btw you can simplify let i=i+1 to ((i++)), but it's even simpler to use a C-style for loop. And also you don't need to define args before adding to it.
So:
#!/bin/bash
for ((i=0; i<5; ++i)); do
args+=($i)
echo "${args[#]}"
done

How to append values to an array in Bash by adding one element to the previous entry?

I have a list of numbers 1 2 3 4 5 that I am trying to organize into an array where the values are in a sequence where the current value is the summation of the previous values in the array (like this): 1 3 6 10 15. My current code is as follows:
array=()
for n in `seq 1 5`
do
if [ $n -eq 1 ]; then
array+=($n)
else
value=$n
index="$(($n-1))"
array+=(`echo ${array[$index]}+$value`)
fi
done
However, when I try checking the array echo "${array[#]}" I get 1 +2 +3 +4 +5. How can I best go about solving this problem?
It is quite simple if you know how to get the last element of the array in bash arrays!. You can just use a negative index ${myarray[-1]} to get the last element. You can do the same thing for the second-last, and so on; in Bash:
fbseries=()
for ((i=1; i<=5; i++)); do
if [ "$i" -eq 1 ]; then
fbseries+=("$i")
else
fbseries+=( $(( ${fbseries[-1]} + $i )) )
fi
done
With the example and some modifications all you need is as above.
You are pretty close the a working code. Here I also added some improvements:
array=()
for n in {1..5}
do
if [ "$n" -eq 1 ]; then
array+=("$n")
else
value="$n"
index="$((n-1))"
array+=($((${array[$index]}+value)))
fi
done
You can avoid using seq, and you don't need an echo but a calculus.
BTW, that's not a Fibonacci serie.

Bash function with array won't work

I am trying to write a function in bash but it won't work. The function is as follows, it gets a file in the format of:
1 2 first 3
4 5 second 6
...
I'm trying to access only the strings in the 3rd word in every line and to fill the array "arr" with them, without repeating identical strings.
When I activated the "echo" command right after the for loop, it printed only the first string in every iteration (in the above case "first").
Thank you!
function storeDevNames {
n=0
b=0
while read line; do
line=$line
tempArr=( $line )
name=${tempArr[2]}
for i in $arr ; do
#echo ${arr[i]}
if [ "${arr[i]}" == "$name" ]; then
b=1
break
fi
done
if [ "$b" -eq 0 ]; then
arr[n]=$name
n=$(($n+1))
fi
b=0
done < $1
}
The following line seems suspicious
for i in $arr ; do
I changed it as follows and it works for me:
#! /bin/bash
function storeDevNames {
n=0
b=0
while read line; do
# line=$line # ?!
tempArr=( $line )
name=${tempArr[2]}
for i in "${arr[#]}" ; do
if [ "$i" == "$name" ]; then
b=1
break
fi
done
if [ "$b" -eq 0 ]; then
arr[n]=$name
(( n++ ))
fi
b=0
done
}
storeDevNames < <(cat <<EOF
1 2 first 3
4 5 second 6
7 8 first 9
10 11 third 12
13 14 second 15
EOF
)
echo "${arr[#]}"
You can replace all of your read block with:
arr=( $(awk '{print $3}' <"$1" | sort | uniq) )
This will fill arr with only unique names from the 3rd word such as first, second, ... This will reduce the entire function to:
function storeDevNames {
arr=( $(awk '{print $3}' <"$1" | sort | uniq) )
}
Note: this will provide a list of all unique device names in sorted order. Removing duplicates also destroys the original order. If preserving the order accept where duplicates are removed, see 4ae1e1's alternative.
You're using the wrong tool. awk is designed for this kind of job.
awk '{ if (!seen[$3]++) print $3 }' <"$1"
This one-liner prints the third column of each line, removing duplicates along the way while preserving the order of lines (only the first occurrence of each unique string is printed). sort | uniq, on the other hand, breaks the original order of lines. This one-liner is also faster than using sort | uniq (for large files, which doesn't seem to be applicable in OP's case), since this one-liner linearly scans the file once, while sort is obviously much more expensive.
As an example, for an input file with contents
1 2 first 3
4 5 second 6
7 8 third 9
10 11 second 12
13 14 fourth 15
the above awk one-liner gives you
first
second
third
fourth
To put the results in an array:
arr=( $(awk '{ if (!seen[$3]++) print $3 }' <"$1") )
Then echo ${arr[#]} will give you first second third fourth.

Bash Array - Cant get input from a loop in to my array correctly

I run this script as
script.sh 'abcdefghijk123456789!##$%^&' 'aaaa4444ged###'
it should be able to produce an array containing something like
1 1 1 1 15 15 15 15 7 5 23 23 23
with 12 indexes 0 - 11, but what i get is
1 1 1 1 1 5 1 5 1 5 1 5 7 5 2 3 2 3 2 3
with 20 indexes.
I want to populate a array with base numbers for a given char.
So i have a list of chars that we will be using in $startstring ie.
abcc5678% we can say that every char in $startstring is = to one char in the
$charset ie. abcd5678!%. This code finds what each $startstring char is equal
to $charset's index number. That information is what i am trying to capture in a
array. Mostly it works except a bug where instead of the whole number 10 getting
stored in decoded[1] what happens is the number 10 is split in to 1 and 0 and
then both are put in under separate indexes. So instead of a "1 10 1" and 3
indexes i end up with 4 indexes with "1 1 0 1". Im sure im just handling my
variables the wrong way but i searched and searched and now my brain is gonna
explode so i came here for some relief. or hope of it anyway. Can someone tell
me the proper way to insert digits in to this decoded[] array?
#!/bin/bash
#declare -i decoded
charset=$1
startstring=$2
start=$((${#charset}-1))
echo "Setting up CharMap to CharSet"
for i in $(eval echo {0..$start})
do
echo $i " = " ${charset:$i:1}
done
echo "Proving Chars Were Mapped Correctly."
start2=$((${#startstring}))
start3=$((${#charset}-1))
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo "found that" ${startstring:$i:1}"=" $p 'from the charmap.'
decoded+=$p #<--### I DONT THINK THIS IS WHAT I NEED ###
fi
done
done
##################Just trying to print my new array#########################
start4=$((${#decoded}-1))
echo 'Testing the array $decoded'
echo 'the var start4(length of $decoded) = '$start4
echo 'this number should equal ----------> '$start2
echo 'Printing out the $decoded array in a for loop'
for c in $(eval echo {0..$start4})
do
echo ${decoded[$c]} ###DOESNT WORK LIKE I THOUGHT# also tried echo ${decode:$c:1}
done
decoded+=$p appends $p as a string, not as an array entry. Essentially, you're creating the string "11111515151575232323" by appending all the index numbers together. (Actually, I get "00001414141464322222227" from your example, because of index bound problems. I'll let you worry about that...)
To store the decoded values as an array, set decoded to an empty array before the loop, and then use decoded+=("$p") to add $p as an element:
decoded=() # create an empty array
for i in $(eval echo {0..$start2})
do
for p in $(eval echo {0..$start3})
do
if [ "${startstring:$i:1}" == "${charset:$p:1}" ]
then
echo ${startstring:$i:1} "=" $p
decoded+=("$p") # append $p as a new array element
fi
done
done
Then, to get the size of the array (rather than a string length), use ${#decoded[#]}:
start4=$((${#decoded[#]}-1))

Resources