How to slice an array in Bash - arrays

Looking the "Array" section in the bash(1) man page, I didn't find a way to slice an array.
So I came up with this overly complicated function:
#!/bin/bash
# #brief: slice a bash array
# #arg1: output-name
# #arg2: input-name
# #args: seq args
# ----------------------------------------------
function slice() {
local output=$1
local input=$2
shift 2
local indexes=$(seq $*)
local -i i
local tmp=$(for i in $indexes
do echo "$(eval echo \"\${$input[$i]}\")"
done)
local IFS=$'\n'
eval $output="( \$tmp )"
}
Used like this:
$ A=( foo bar "a b c" 42 )
$ slice B A 1 2
$ echo "${B[0]}" # bar
$ echo "${B[1]}" # a b c
Is there a better way to do this?

See the Parameter Expansion section in the Bash man page. A[#] returns the contents of the array, :1:2 takes a slice of length 2, starting at index 1.
A=( foo bar "a b c" 42 )
B=("${A[#]:1:2}")
C=("${A[#]:1}") # slice to the end of the array
echo "${B[#]}" # bar a b c
echo "${B[1]}" # a b c
echo "${C[#]}" # bar a b c 42
echo "${C[#]: -2:2}" # a b c 42 # The space before the - is necesssary
Note that the fact that a b c is one array element (and that it contains an extra space) is preserved.

There is also a convenient shortcut to get all elements of the array starting with specified index. For example "${A[#]:1}" would be the "tail" of the array, that is the array without its first element.
version=4.7.1
A=( ${version//\./ } )
echo "${A[#]}" # 4 7 1
B=( "${A[#]:1}" )
echo "${B[#]}" # 7 1

Array slicing like in Python (From the rebash library):
array_slice() {
local __doc__='
Returns a slice of an array (similar to Python).
From the Python documentation:
One way to remember how slices work is to think of the indices as pointing
between elements, with the left edge of the first character numbered 0.
Then the right edge of the last element of an array of length n has
index n, for example:
```
+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1
```
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 1:-2 "${a[#]}")
1 2 3
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 0:1 "${a[#]}")
0
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice 1:1 "${a[#]}")" ] && echo empty
empty
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice 2:1 "${a[#]}")" ] && echo empty
empty
>>> local a=(0 1 2 3 4 5)
>>> [ -z "$(array.slice -2:-3 "${a[#]}")" ] && echo empty
empty
>>> [ -z "$(array.slice -2:-2 "${a[#]}")" ] && echo empty
empty
Slice indices have useful defaults; an omitted first index defaults to
zero, an omitted second index defaults to the size of the string being
sliced.
>>> local a=(0 1 2 3 4 5)
>>> # from the beginning to position 2 (excluded)
>>> echo $(array.slice 0:2 "${a[#]}")
>>> echo $(array.slice :2 "${a[#]}")
0 1
0 1
>>> local a=(0 1 2 3 4 5)
>>> # from position 3 (included) to the end
>>> echo $(array.slice 3:"${#a[#]}" "${a[#]}")
>>> echo $(array.slice 3: "${a[#]}")
3 4 5
3 4 5
>>> local a=(0 1 2 3 4 5)
>>> # from the second-last (included) to the end
>>> echo $(array.slice -2:"${#a[#]}" "${a[#]}")
>>> echo $(array.slice -2: "${a[#]}")
4 5
4 5
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -4:-2 "${a[#]}")
2 3
If no range is given, it works like normal array indices.
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -1 "${a[#]}")
5
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice -2 "${a[#]}")
4
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 0 "${a[#]}")
0
>>> local a=(0 1 2 3 4 5)
>>> echo $(array.slice 1 "${a[#]}")
1
>>> local a=(0 1 2 3 4 5)
>>> array.slice 6 "${a[#]}"; echo $?
1
>>> local a=(0 1 2 3 4 5)
>>> array.slice -7 "${a[#]}"; echo $?
1
'
local start end array_length length
if [[ $1 == *:* ]]; then
IFS=":"; read -r start end <<<"$1"
shift
array_length="$#"
# defaults
[ -z "$end" ] && end=$array_length
[ -z "$start" ] && start=0
(( start < 0 )) && let "start=(( array_length + start ))"
(( end < 0 )) && let "end=(( array_length + end ))"
else
start="$1"
shift
array_length="$#"
(( start < 0 )) && let "start=(( array_length + start ))"
let "end=(( start + 1 ))"
fi
let "length=(( end - start ))"
(( start < 0 )) && return 1
# check bounds
(( length < 0 )) && return 1
(( start < 0 )) && return 1
(( start >= array_length )) && return 1
# parameters start with $1, so add 1 to $start
let "start=(( start + 1 ))"
echo "${#: $start:$length}"
}
alias array.slice="array_slice"

At the risk of beating a dead horse, I was inspired by #jandob's answer and made this version that
Is simpler (doesn't have so much shift logic or rewriting of variables as often).
Respects quoted strings without dealing with IFS (-r mode only).
Allows the user to specify [start, end) slicing or [start, length] slicing via -l flag.
Allows you to echo the resulting array (default behavior), or "return" it into a new array for use in the calling parent (via -r slicedArray).
Note: namerefs are only supported in Bash >= 4.3. To support earlier versions of Bash (i.e. Mac without Brew's bash), you'll need to use indirection instead: use a temp var to access array parameters, e.g. declare arrValuesCmd="$1[#]"; declare arr=("${!arrValuesCmd}"), and use eval for return values, e.g. eval $retArrName='("${newArr[#]}")' (note the single quotes around the array declaration).
array.slice() {
# array.slice [-l] [-r returnArrayName] myArray 3 5
# Default functionality is to use second number as end index for slice (exclusive).
# Can instead use second number as length by passing `-l` flag.
# `echo` doesn't maintain quoted entries, so pass in `-r returnArrayName` to keep them.
declare isLength
declare retArrName
declare OPTIND=1
while getopts "lr:" opt; do
case "$opt" in
l)
# If `end` is slice length instead of end index
isLength=true
;;
r)
retArrName="$OPTARG"
;;
esac
done
shift $(( OPTIND - 1 ))
declare -n arr="$1"
declare start="$2"
declare end="$3"
declare arrLength="${#arr[#]}"
declare newArr=()
declare newArrLength
# Bash native slicing:
# Positive index values: ${array:start:length}
# Negative index values: ${array: start: length}
# To use negative values, a space is required between `:` and the variable
# because `${var:-3}` actually represents a default value,
# e.g. `myVar=${otherVal:-7}` represents (pseudo-code) `myVar=otherVal || myVar=7`
if [[ -z "$end" ]]; then
# If no end is specified (regardless of `-l`/length or index), default to the rest of the array
newArrLength="$arrLength"
elif [[ -n "$isLength" ]]; then
# If specifying length instead of end-index, use native bash array slicing
newArrLength="$(( end ))"
else
# If specifying end-index, use custom slicing based on a range of [start, end):
newArrLength="$(( end - start ))"
fi
newArr=("${arr[#]: start: newArrLength}")
if [[ -n "$retArrName" ]]; then
declare -n retArr="$retArrName"
retArr=("${newArr[#]}")
else
echo "${newArr[#]}"
fi
}
Examples:
myArray=(x y 'a b c' z 5 14) # length=6
array.slice myArray 2 4
# > a b c z
array.slice -l myArray 3 2
# > z 5
# Note: Output was manually quoted to show the result more clearly.
# Actual stdout content won't contain those quotes, which is
# why the `-r returnArray` option was added.
array.slice -r slicedArray myArray -5 -3 # equivalent of [2, 4)
# > (null)
echo -e "myArray (length=${#myArray[#]}): ${myArray[#]} \nslicedArray (length=${#slicedArray[#]}): ${slicedArray[#]}"
# > myArray (length=6): x y 'a b c' z 5 14
# > slicedArray (length=2): 'a b c' z
array.slice -lr slicedArray myArray -5 3 # length instead of index, equivalent of [2, 5)
# > (null)
echo -e "myArray (length=${#myArray[#]}): ${myArray[#]} \nslicedArray (length=${#slicedArray[#]}): ${slicedArray[#]}"
# > myArray (length=6): x y 'a b c' z 5 14
# > slicedArray (length=3): 'a b c' z 5

Related

Print an element from a sorted array

I wrote a program to sort an array and print the element where the unsorted array and sorted array matches. In theory, this should work, but it isn't. All the elements in the sorted array for some reason combine it all and output an array element of 1.
#!/bin/bash
arr=(6 2 15 90 9 1 4 30 1 3)
function sort(){
local array=($#) max=$(($# - 1))
while ((max > 0))
do
local i=0
while ((i < max)); do
if [ ${array[$i]} \> ${array[$((i + 1))]} ]
then
local t=${array[$i]}
array[$i]=${array[$((i + 1))]}
array[$((i + 1))]=$t
fi
((i++))
done
((max--))
done
echo ${array[#]}
}
arr_sort=($(sort ${arr[#]}))
for ((j=0; j<(( ${#arr[#]} -1 )); j++)); do
for ((k=0; k<(( ${#arr[#]} -1 )); k++)); do
if (( ${arr[j]:-0} == ${arr_sort[k]:-0} )); then
echo ${arr[j]}
break
fi
done
Try this:
#!/bin/bash
arr=(6 2 15 90 9 1 4 30 1 3)
arr_sort=( $(echo ${arr[#]} | tr ' ' '\n' | sort -n) )
for ((j=0; j<${#arr[#]}; j++)); do
if (( ${arr[i]} == ${arr_sort[i]} )); then
echo "Match ${arr[j]} at position $j (starting from 0)"
fi
done
Since there are no matches between the unsorted array
6 2 15 90 9 1 4 30 1 3
and the sorted one
1 1 2 3 4 6 9 15 30 90
in the example you gave, you will have no output.

Bash Array Manipulation

I doing a project for a Unix/Linux class and have a few Arrays in another file, like this
All the arrays should store is a 1 or 0
T1=( 1 1 1 1 1 )
T2=( 1 1 1 1 1 )
T3=( 1 1 1 1 1 )
T4=( 1 1 1 1 1 )
T5=( 1 1 1 1 1 )
However i'm having difficulty editing the arrays to 0 and making changes stick
#!/bin/bash
i=0
for line in `cat JeopardyOut`
do
let i=i+1
#Creating one big Array with all the values from the arrays file
Array[i]=$line
done
cat JeopardyOut
#Parsing the giant Array into the main 5 arrays I'm using
createArray()
{
y=0
for y in 0 1 2 3 4
do
for i in 2 3 4 5 6
do
#echo "${Array[i]}"
T1[$y]=${Array[i]}
done
for i in 9 10 11 12 13
do
#echo "${Array[i]}"
T2[$y]=${Array[i]}
done
for i in 16 17 18 19 20
do
#echo "${Array[i]}"
T3[$y]=${Array[i]}
done
for i in 23 24 25 26 27
do
#echo "${Array[i]}"
T4[$y]=${Array[i]}
done
for i in 30 31 32 33 34
do
#echo "${Array[i]}"
T5[$y]=${Array[i]}
done
done
}
createArray
ArrayNum=$1
Index=$2
There's likely way better ways to do this, However this is what ended up working for me.
#Changing the necessary indexes, this will be used by a completely
#different script
ChangeArray()
{
if [[ $ArrayNum == "1" ]]; then
T1[ $Index ]=0
elif [[ $ArrayNum == "2" ]]; then
T2[ $Index ]=0
elif [[ $ArrayNum == "3" ]]; then
T3[ $Index ]=0
elif [[ $ArrayNum == "4" ]]; then
T4[ $Index ]=0
elif [[ $ArrayNum == "5" ]]; then
T5[ $Index ]=0
else
echo "Invalid Parameters"
fi
}
if [[ $ArrayNum -ne "" || $Index -ne "" ]]; then
if [[ $ArrayNum == "5" && $Index == "5"]]; then
reset
else
ChangeArray
fi
fi
# And the part that's likely at fault for my issue but don't know how I
# should fix it
echo "T1=( ${T1[*]} )" > JeopardyOut
echo "T2=( ${T2[*]} )" >> JeopardyOut
echo "T3=( ${T3[*]} )" >> JeopardyOut
echo "T4=( ${T4[*]} )" >> JeopardyOut
echo "T5=( ${T5[*]} )" >> JeopardyOut
cat JeopardyOut
Something is wrong with the way I am trying to edit the Arrays...
While I can get any index of any of the arrays to 0, I do not know why the 1s I change to 0 turn back into 1 when I rerun the script.
PS. This is a basis class for Linux programming in Sierra, I don't really understand a lot of the bash script other than what I've learned through trial and error.
Maybe this example can help you
#!/bin/bash
while read -r line; do
#Creating one big Array with all the values from the arrays file
Array+=(`grep -oP '\(\K(.+?)(?=\))' <<< "$line"`)
done < JeopardyOut
echo "Giant Array value: ${Array[#]}"
# Or you can clone the arrays in the file
i=0
while read -r line; do
((i++))
eval "T$i=(`grep -oP '\(\K(.+?)(?=\))' <<< "$line"`)"
done < JeopardyOut
echo "This is the contents of array T1: ${T1[#]}"
echo "This is the contents of array T2: ${T2[#]}"
echo "This is the contents of array T3: ${T3[#]}"
echo "This is the contents of array T4: ${T4[#]}"
echo "This is the contents of array T5: ${T5[#]}"
# This can help you to make a new file
# I change some value to our arrays
T1[2]=0
T2[1]=true
# Now let's go to make a new arrays file
echo "T1=(${T1[#]})" > JeopardyOut2
echo "T2=(${T2[#]})" >> JeopardyOut2
echo "T3=(${T3[#]})" >> JeopardyOut2
echo "T4=(${T4[#]})" >> JeopardyOut2
echo "T5=(${T5[#]})" >> JeopardyOut2
echo "This is the content of JeopardyOut2:"
cat JeopardyOut2
Output
$ bash example
Giant Array value: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
This is the contents of array T1: 1 1 1 1 1
This is the contents of array T2: 1 1 1 1 1
This is the contents of array T3: 1 1 1 1 1
This is the contents of array T4: 1 1 1 1 1
This is the contents of array T5: 1 1 1 1 1
This is the content of JeopardyOut2:
T1=(1 1 0 1 1)
T2=(1 true 1 1 1)
T3=(1 1 1 1 1)
T4=(1 1 1 1 1)
T5=(1 1 1 1 1)
$

Shell script: How to save loop output to array

This is my simple shell script. The objective is to split string "RANDOM948" as array so that I can manipulate any character in string "RANDOM948".
ubuntu#Ubuntu:~$ cat -n longscript.sh
1 string="RANDOM948"
2
3 c1=${string:0:1}
4 c2=${string:1:1}
5 c3=${string:2:1}
6 c4=${string:3:1}
7 c5=${string:4:1}
8 c6=${string:5:1}
9 c7=${string:6:1}
10 c8=${string:7:1}
11 c9=${string:8:1}
12
13 echo $c9 $c4 $c1
ubuntu#Ubuntu:~$
ubuntu#Ubuntu:~$ ./longscript.sh
8 D R
ubuntu#Ubuntu:~$
I believe this can be simplify by using for loop. This is my attempt. However, I have no idea how to save the loop output as array.
ubuntu#Ubuntu:~$ cat -n testscript.sh
1 string="RANDOM948"
2
3 for i in {1..9}
4 do
5 echo c$i=$\{string:`expr $i - 1`:1}
6 done
7
8 # echo $c9 $c4 $c1
ubuntu#Ubuntu:~$
ubuntu#Ubuntu:~$ ./testscript.sh
c1=${string:0:1}
c2=${string:1:1}
c3=${string:2:1}
c4=${string:3:1}
c5=${string:4:1}
c6=${string:5:1}
c7=${string:6:1}
c8=${string:7:1}
c9=${string:8:1}
ubuntu#Ubuntu:~$
UPDATE: New code as advised by #sos
I've updated this code with new line (5 & 6), however it still doesn't work
ubuntu#Ubuntu:~$ cat -n testscript.sh
1 string="RANDOM948"
2
3 for i in {1..9}
4 do
5 typeset -a c
6 c[${i}]=$\{string:`expr $i - 1`:1}
7 done
8
9 echo TEST OUTPUT $c9 $c4 $c1
ubuntu#Ubuntu:~$
ubuntu#Ubuntu:~$
ubuntu#Ubuntu:~$ ./testscript.sh
TEST OUTPUT
ubuntu#Ubuntu:~$
replace
echo c${i}
with
typeset -a c # declare a indexed array
c[${i}]=...
Here's what I get
set -x
for i in {1..9}
do
c[${i}]=${string:`expr $i - 1`:1}
done
set +x
+ expr 1 - 1
+ c[1]=R
+ expr 2 - 1
+ c[2]=A
+ expr 3 - 1
+ c[3]=N
+ expr 4 - 1
+ c[4]=D
+ expr 5 - 1
+ c[5]=O
+ expr 6 - 1
+ c[6]=M
+ expr 7 - 1
+ c[7]=9
+ expr 8 - 1
+ c[8]=4
+ expr 9 - 1
+ c[9]=8
Note: It's considered "unusual" to begin an array index at 1 rather than 0
you will do yourself and others a favor if you identify what is running your script by using a shebang line e.g.
#!/bin/bash
as the first line in your script. In any case, the below example may help you.
cat ./looptest.sh
note the #! line....
#!/bin/bash
for i in {0..9}
do
myArray[$i]="hello_$i"
echo set the value of myArray[$i]
done
echo the value of myArray[4] is ${myArray[4]}
Here's the output:
set the value of myArray[0]
set the value of myArray[1]
set the value of myArray[2]
set the value of myArray[3]
set the value of myArray[4]
set the value of myArray[5]
set the value of myArray[6]
set the value of myArray[7]
set the value of myArray[8]
set the value of myArray[9]
the value of myArray[4] is hello_4

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