2

Say we have a BASH array with integers:

declare -a arr=( 1 2 3 )

and I want to do an arithmetic operation on each element, e.g. add 1. Is there an altenative to a for loop:

for (( i=0 ; i<=${#arr[@]}-1 ; i++ )) ; do
    arr[$i]=$(( ${arr[$i]} + 1 ))
done

I have tried a few options:

arr=$(( ${arr[@]} + 1 ))

fails, while

arr=$(( $arr + 1 ))

results in

echo ${arr[@]}
2 2 3

thus only the first (zeroth) element being changed.

I read about awk solutions, but would like to know if BASH arithmetics support such batch operations on each array element.

FelixJN
  • 540
  • 3
  • 16
  • 2
    I don't think there is an alternative. As a side note, none of the programming languages I use can do that natively. Also, you can simplify `arr[$i]=$(( ${arr[$i]} + 1 ))` to `arr[$i]++` – Aserre Jul 30 '15 at 07:58
  • 1
    I do not know about your preferred languages, but e.g. FORTRAN does support `arr(:) = arr(:) + 1` or `arr3(:) = arr1(:) * arr2(:)` , so I had the hope that BASH would be capable. Sorry, I cannot reproduce the `arr[$i]++` thing. Care to explain? – FelixJN Jul 30 '15 at 08:06
  • 1
    forgot the parenthesis. correct syntax is `(( $arr[$i]++ ))` – Aserre Jul 30 '15 at 08:19
  • The shell would only be looping in the background anyway if there were a function. So if it bothers you just write your own function to do it. – 123 Jul 30 '15 at 09:02
  • @Plutox sorry, still cannot reproduce, could you explicitly write the for loop start to end? Thanks in advance. – FelixJN Jul 30 '15 at 09:17
  • @User112638726 Not a matter of speed, just in terms of readability and being nosy how far BASH arithmetics could take me. – FelixJN Jul 30 '15 at 09:18

2 Answers2

4

I know your question is not fresh new but you can accomplish what you want by declaring your array as integer and then applying a substitution:

declare -ia arr=( 1 2 3 )
value=1

declare -ia 'arr_added=( "${arr[@]/%/+$value}" )'
echo "arr_added: ${arr_added[*]}"

value=42
declare -ia 'arr_added=( "${arr[@]/%/+$value}" )'
echo "arr_added: ${arr_added[*]}"

It outputs:

arr_added: 2 3 4
arr_added: 43 44 45

You can perform other math operations as well:

value=3
declare -ia 'arr_multd=( "${arr[@]/%/*$value}" )'
echo "arr_multd: ${arr_multd[*]}"

Outputs:

arr_multd: 3 6 9
j4x
  • 3,595
  • 3
  • 33
  • 64
  • 1
    Thank you - in my tests also works without using quotes, e.gg `declare -ia new_arr=( ${arr[@]}/%/+$value }`. Is there any documentation regarding this? I didn't see it in `man bash`. – FelixJN Sep 16 '17 at 19:27
  • Yes, there is no direct mention for `-i` in array documentation neither in any man page nor TLDP or the like. You can check `-i`, though in "Typing variables: declare or typeset" [http://tldp.org/LDP/abs/html/declareref.html]. It works for any variable, including arrays. – j4x Sep 18 '17 at 13:33
-1

you can use eval to have the feeling of lambda function (not sure about the syntax but this should be the main idea ) :

eval "function add1 { x=$1; y=$((x+1)) ; return $y; }"
for (( i=0 ; i<=${#arr[@]}-1 ; i++ )) ; do
    add1 ${arr[i]}
done
Zohar81
  • 4,554
  • 5
  • 29
  • 82
  • 1
    I don't think this answers the question. OP wants to avoid a for loop altogether – Aserre Jul 30 '15 at 08:20
  • It also is a pointless use of `eval`, since the parameter expansions will be expanded before the function is ever defined. `return` is used to return an exit status, not a computation. – chepner Jul 30 '15 at 12:52