bad substitution shell- trying to use variable as name of array - arrays

#!/bin/bash
array=( 2 4 5 8 15 )
a_2=( 2 4 8 10 )
a_4=( 2 4 8 10 )
a_5=( 10 12 )
a_8=( 8 12 )
a_15=( 2 4 )
numberOfTests=5
while [ $i -lt ${#array[#]} ]; do
j=0
currentArray =${array[$i]}
*while [ $j -lt ${#a_$currentArray [#]} ]; do #### this line i get ->>>> bad substitution*
./test1.sh "${array[$i]}" -c "${a_"$currentArray "[$j]}" &
let j=j+1
done
let i=i+1
done
so Im trying this code, loop over an array(called array), The array should point out
the array number we are now looping(a_X). And every time to point out the current place and value.
can anybody help me how im using the $currentArray to work properly so I can know the length of the array and the value?
I get in the line I marked an error.
Thank you guys!

The simplest solution is to store the full names of the arrays, not just the numerical suffix, in array. Then you can use indirect parameter expansion while iterating directly over the values, not the indices, of the arrays.
# Omitting numberOfTests has it does not seem to be used
array=(a_2 a_4 a_5 a_8 a_15)
a_2=( 2 4 8 10 )
a_4=( 2 4 8 10 )
a_5=( 10 12 )
a_8=( 8 12 )
a_15=( 2 4 )
for arr in "${array[#]}"; do
currentArray=$arr[#]
for value in "${!currentArray}"; do
./test1.h "${arr#a_}" -c "$value" &
done
done

Related

'if' in array w/ Shell Script

I have an array with different values, and if the value is 3 (integer) this value will be exchanged for 999 (integer). But I have a syntax problem in the 'if' block of the array.
The correct return would be: 1 999 5 7 9 999
vetor=(1 3 5 7 9 3)
for i in ${vetor[*]}
do
if [[ ${vetor[i]} = 3 ]]; then
${vetor[i]} = 999
fi
echo $i
done
This produces the correct output in Bash:
vetor=(1 3 5 7 9 3);
for i in ${!vetor[*]};
do
if [[ ${vetor[i]} -eq 3 ]]; then
vetor[i]=999;
fi;
echo ${vetor[i]};
done
I added ! in the for loop expression to get the indices of vetor instead of the values, and I removed the ${} around the assignment in the if condition (this was giving "if 3 is not a typo" warnings). Also changed the echo to get the value at vetor[i] instead of printing the index.

delete elements in array bash

im trying to delete two elements at same time in my array in bash script
my code is
elegidos = (1 2 3 4 5 6 7)
i=0
j=${#elegidos[#]}
delete=($i $j)
while [ $i -le $j ]; do
#elegidosmenosdos=${elegidos[#]/$i:$j}
echo ${elegidos[#]/$delete}
delete=($i $j)
let "i++"
let "j--"
done
the output that i have is
1 2 3 4 5 6 7
1 2 3 4 5 6 7
2 3 4 5 6 7
1 3 4 5 6 7
and i need 21 different combinations with five elements using seven numbers
output example
1 2 3 4 5
2 3 4 5 6
7 6 5 4 3
.
.
.
.
.(21 MORE)
Well, it took a minute to suss out that you were simply wanting to do an end-to-middle delete of two-elements at a time working from the ends of the array, deleting the end nodes at the same time, increment/decrement your counters and repeat until you reached the middle. Why you want to do it this way is a bit of a mystery. You can, of course, simply unset elegidos and unset all values at once. However, if you want to work in from both ends -- that fine too if you have a purpose for it.
You have several problems in your script. In bash, all arrays are zero indexed. Your array has 7-elements, so the valid indexes are 0-6. Therefore, you j was wrong to begin with. You needed to subtract 1 from the number of elements to get the index for the end-element, e.g.
i=0
j=$((${#elegidos[#]} - 1))
bash provides a C-style loop that can greatly simplify your task. While you are free to use a while a C-style loop can handle the index increment and decrement seamlessly, e.g.
for ((; i <= j; i++, j--)); do
note the i <= j. If you have an odd-number of elements in the array, on your last iteration, you will simply be deleting one-value instead of two. To handle that condition you need a simple test within the loop to check whether [ "$i" = "$j" ] (or using the arithmetic comparison (( i == j ))).
Putting that altogether, you could refine your element removal to empty the array two-elements at a time to something similar to the following:
#!/bin/bash
elegidos=(1 2 3 4 5 6 7)
i=0
j=$((${#elegidos[#]} - 1))
delete=($i $j)
for ((; i <= j; i++, j--)); do
declare -p elegidos
unset elegidos[$i]
[ "$i" != "$j" ] && unset elegidos[$j]
delete=($i $j)
done
Example Use/Output
$ bash array_del.sh
declare -a elegidos='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7")'
declare -a elegidos='([1]="2" [2]="3" [3]="4" [4]="5" [5]="6")'
declare -a elegidos='([2]="3" [3]="4" [4]="5")'
declare -a elegidos='([3]="4")'
You can see above, that on the first 3-iterations, both end-elements are removed. However, notice on the last removal (there originally being and odd number of elements, only the single-last value is removed on the final iteration.
Look things over and let me know if I captured what you were attempting and whether you have any further questions. If your intent was something else, drop a comment and I'm happy to help further.

How to identify the position of a sequence of elements in an array in bash

I'm learning bash and I'm trying identify a sequence elements (patch or subarray) position into a array.
For example:
array=(9 5 8 3 2 7 5 9 0 1 1 5 4 3 8 9 6 2 6 5 7 9 8);
patch=(0 1 1 5)
I would like to obtain a output equals to 8 (start position of my patch in relation to array) or 11 (final position).
bash doesn't really have any built-in facility to do this; you need to walk the array yourself:
for ((i=0; i<${#array[#]}; i++)); do
for ((j=0; j<${#patch[#]}; j++)); do
# Make sure the corresponding elements
# match, or give up. RHS is quoted to ensure
# actual string equality, rather than just pattern matching
[[ ${array[i+j] == "${patch[j]}" ]] || break
done
if [[ $j == ${#patch[#] ]]; then
# All the comparisons succeeded!
start=$i
finish=$((i+j-1))
break
fi
done

How to add a value into the middle of an array?

Hello I have the following array:
array=(1 2 3 4 5 7 8 9 12 13)
I execute this for loop:
for w in ${!array[#]}
do
comp=$(echo "${array[w+1]} - ${array[w]} " | bc)
if [ $comp = 1 ]; then
/*???*/
else
/*???*/
fi
done
What I would like to do is to insert a value when the difference between two consecutive elements is not = 1
How can I do it?
Many thanks.
Just create a loop from the minimum to the maximum values and fill the gaps:
array=(1 2 3 4 5 7 8 9 12 13)
min=${array[0]}
max=${array[-1]}
new_array=()
for ((i=min; i<=max; i++)); do
if [[ " ${array[#]} " =~ " $i " ]]; then
new_array+=($i)
else
new_array+=(0)
fi
done
echo "${new_array[#]}"
This creates a new array $new_array with the values:
1 2 3 4 5 0 7 8 9 0 0 12 13
This uses the trick in Check if an array contains a value.
You can select parts of the original array with ${arr[#]:index:count}.
Select the start, insert a new element, add the end.
To insert an element after index i=5 (the fifth element)
$ array=(1 2 3 4 5 7 8 9 12 13)
$ i=5
$ arr=("${array[#]:0:i}") ### take the start of the array.
$ arr+=( 0 ) ### add a new value ( may use $((i+1)) )
$ arr+=("${array[#]:i}") ### copy the tail of the array.
$ array=("${arr[#]}") ### transfer the corrected array.
$ printf '<%s>' "${array[#]}"; echo
<1><2><3><4><5><6><7><8><9><12><13>
To process all the elements, just do a loop:
#!/bin/bash
array=(1 2 3 4 5 7 8 9 12 13)
for (( i=1;i<${#array[#]};i++)); do
if (( array[i] != i+1 ));
then arr=("${array[#]:0:i}") ### take the start of the array.
arr+=( "0" ) ### add a new value
arr+=("${array[#]:i}") ### copy the tail of the array.
# echo "head $i ${array[#]:0:i}" ### see the array.
# echo "tail $i ${array[#]:i}"
array=("${arr[#]}") ### transfer the corrected array.
fi
done
printf '<%s>' "${array[#]}"; echo
$ chmod u+x ./script.sh
$ ./script.sh
<1><2><3><4><5><0><7><8><9><10><0><0><13>
There does not seem to be a way to insert directly into an array. You can append elements to another array instead though:
result=()
for w in ${!array[#]}; do
result+=("${array[w]}")
comp=$(echo "${array[w+1]} - ${array[w]} " | bc)
if [ $comp = 1 ]; then
/* probably leave empty */
else
/* handle missing digits */
fi
done
As a final step, you can assign result back to the original array.

How can I find the sum of the elements of an array in Bash?

I am trying to add the elements of an array that is defined by user input from the read -a command. How can I do that?
read -a array
tot=0
for i in ${array[#]}; do
let tot+=$i
done
echo "Total: $tot"
Given an array (of integers), here's a funny way to add its elements (in bash):
sum=$(IFS=+; echo "$((${array[*]}))")
echo "Sum=$sum"
e.g.,
$ array=( 1337 -13 -666 -208 -408 )
$ sum=$(IFS=+; echo "$((${array[*]}))")
$ echo "$sum"
42
Pro: No loop, no subshell!
Con: Only works with integers
Edit (2012/12/26).
As this post got bumped up, I wanted to share with you another funny way, using dc, which is then not restricted to just integers:
$ dc <<< '[+]sa[z2!>az2!>b]sb1 2 3 4 5 6 6 5 4 3 2 1lbxp'
42
This wonderful line adds all the numbers. Neat, eh?
If your numbers are in an array array:
$ array=( 1 2 3 4 5 6 6 5 4 3 2 1 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]}lbxp"
42
In fact there's a catch with negative numbers. The number '-42' should be given to dc as _42, so:
$ array=( -1.75 -2.75 -3.75 -4.75 -5.75 -6.75 -7.75 -8.75 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]//-/_}lbxp"
-42.00
will do.
Pro: Works with floating points.
Con: Uses an external process (but there's no choice if you want to do non-integer arithmetic — but dc is probably the lightest for this task).
My code (which I actually utilize) is inspired by answer of gniourf_gniourf. I personally consider this more clear to read/comprehend, and to modify. Accepts also floating points, not just integers.
Sum values in array:
arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' sum=$(echo "scale=1;${arr[*]}"|bc)
echo $sum # 55
With small change, you can get the average of values:
arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' avg=$(echo "scale=1;(${arr[*]})/${#arr[#]}"|bc)
echo $avg # 5.5
gniourf_gniourf's answer is excellent since it doesn't require a loop or bc. For anyone interested in a real-world example, here's a function that totals all of the CPU cores reading from /proc/cpuinfo without messing with IFS:
# Insert each processor core count integer into array
cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
# Read from the array and replace the delimiter with "+"
# also insert 0 on the end of the array so the syntax is correct and not ending on a "+"
read <<< "${cpuarray[#]/%/+}0"
# Add the integers together and assign output to $corecount variable
corecount="$((REPLY))"
# Echo total core count
echo "Total cores: $corecount"
I also found the arithmetic expansion works properly when calling the array from inside the double parentheses, removing the need for the read command:
cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
corecount="$((${cpuarray[#]/%/+}0))"
echo "Total cores: $corecount"
Generic:
array=( 1 2 3 4 5 )
sum="$((${array[#]/%/+}0))"
echo "Total: $sum"
I'm a fan of brevity, so this is what I tend to use:
IFS="+";bc<<<"${array[*]}"
It essentially just lists the data of the array and passes it into BC which evaluates it. The "IFS" is the internal field separate, it essentially specifies how to separate arrays, and we said to separate them with plus signs, that means when we pass it into BC, it receives a list of numbers separated by plus signs, so naturally it adds them together.
Another dc & bash method:
arr=(1 3.88 7.1 -1)
dc -e "0 ${arr[*]/-/_} ${arr[*]/*/+} p"
Output:
10.98
The above runs the expression 0 1 3.88 7.1 _1 + + + + p with dc. Note the dummy value 0 because there's one too many +s, and also note the usual negative number prefix - must be changed to _ in dc.
arr=(1 2 3) //or use `read` to fill the array
echo Sum of array elements: $(( ${arr[#]/%/ +} 0))
Sum of array elements: 6
Explanation:
"${arr[#]/%/ +}" will return 1 + 2 + 3 +
By adding additional zero at the end we will get 1 + 2 + 3 + 0
By wrapping this string with BASH's math operation like this$(( "${arr[#]/%/ +} 0")), it will return the sum instead
This could be used for other math operations.
For subtracting just use - instead
For multiplication use * and 1 instead of 0
Can be used with logic operators too.
BOOL AND EXAMPLE - check if all items are true (1)
arr=(1 0 1)
if [[ $((${arr[#]/%/ &} 1)) -eq 1 ]]; then echo "yes"; else echo "no"; fi
This will print: no
BOOL OR EXAMPLE - check if any item is true (1)
arr=(1 0 0)
if [[ $((${arr[#]/%/ |} 0)) -eq 1 ]]; then echo "yes"; else echo "no"; fi
This will print: yes
A simple way
function arraySum
{
sum=0
for i in ${a[#]};
do
sum=`expr $sum + $i`
done
echo $sum
}
a=(7 2 3 9)
echo -n "Sum is = "
arraySum ${a[#]}
I find this very simple using an increasing variable:
result2=0
for i in ${lineCoffset[#]};
do
result2=$((result2+i))
done
echo $result2

Resources