ClimateUnboxed
ClimateUnboxed

Reputation: 8087

Performing an operation on a list of numbers with bc without using a loop

I have a list of numbers that I want to perform an operation on in BASH (e.g. sine, sqrt etc). At the moment I loop over the vector of numbers using bc and tack on a space " ", which seems a bit clunky:

x=`seq 1 2.5 30`  # generate a list of numbers
for i in $x ; do 
  a=${a}`echo "sqrt($i)" | bc`" "
done # a is output vector

I was wondering if there was a neater way to do this without using the loop and " " tagging?

Upvotes: 1

Views: 213

Answers (3)

glenn jackman
glenn jackman

Reputation: 246847

Using bash, particularly mapfile to stash the output of a command into an array:

$ mapfile -t nums < <(seq 1 2.5 30)

$ mapfile -t sqrts < <(printf "sqrt(%f)\n" "${nums[@]}" | bc -l)

$ printf "%s\n" "${sqrts[@]}"
1
1.87082869338697069279
2.44948974278317809819
2.91547594742265023543
3.31662479035539984911
3.67423461417476714729
4.00000000000000000000
4.30116263352131338586
4.58257569495584000658
4.84767985741632901407
5.09901951359278483002
5.33853912601565560540

Upvotes: 1

Benjamin W.
Benjamin W.

Reputation: 52162

You're not building an array, but a string with spaces. You could use an actual array instead:

for x in $(seq 1 2.5 30); do
    a+=( $(bc <<< "sqrt($x)") )
done

printf '%s\n' "${a[@]}"

resulting in

1
1.8
2.4
2.9
3.3
3.6
4.0
4.3
4.5
4.8
5.0
5.3

Alternatively, you can write it completely in bc to avoid spawning a subshell for each line:

#!/usr/bin/bc

for (x = 1; x <= 30; x += 2.5) {
    sqrt(x)
}
quit

If you stuff that into a script called getsquares, you can get your array with

a=($(./getsquares))

or, best of both worlds (single instance of bc, embedded in Bash script):

a=($(bc <<< 'for (x = 1; x <= 30; x += 2.5) sqrt(x)'))

Upvotes: 2

anubhava
anubhava

Reputation: 785276

Rather that invoking bc for each number, you can use a single awk like this:

awk -v b=1 -v e=30 'BEGIN{for (i=b; i<=e; i+=2.5) printf "%.1f\n", sqrt(i)}'
1.0
1.9
2.4
2.9
3.3
3.7
4.0
4.3
4.6
4.8
5.1
5.3

To store output in an array use:

arr=($(awk -v b=1 -v e=30 'BEGIN{for (i=b; i<=e; i+=2.5) printf "%.1f\n", sqrt(i)}'))

then print output using:

printf '%s\n' "${arr[@]}"

Upvotes: 1

Related Questions