Question

I have a line of numbers, and I want to be able to process them one by one. For ex.

3829 4837 3729 2874 3827 

I want to be able to get the sum, largest number, anything. But I'm not sure how to get each number to process it. I would REAAAAALLLLLLLLY like to stick with pure bash, I don't mind a convoluted solution in bash. But if awk is absolutely necessary I will use it. I don't want to use sed.

I think I could do something like:

max= cut -d" " -f1
in line L cut -d" " -f$(loop index) 
#check for max

I'm confused.

Edit: Thanks for your answers, I've seen some new things I've never seen in bash and I'm ready to explore them more. I received the info I sought and even more! :)

Was it helpful?

Solution

If you want to process process numbers one by one, taking advantage of shell word splitting :

numbers="3829 4837 3729 2874 3827"
 for n in $numbers; do
     # do something with "$n"
done

SUM :

numbers="3829 4837 3729 2874 3827"
echo $((${numbers// /+}))

or

numbers="3829 4837 3729 2874 3827"
for n in $numbers; do
     ((sum+=n))
done

echo $sum

LARGEST :

numbers="3829 4837 3729 2874 3827"
for n in $numbers; do
     ((max<n)) && max=$n
done

echo $max

Alternatively, if you want a global SUM with some shell tricks :

$ tr ' ' '+' <<< '3829 4837 3729 2874 3827' | bc                                 
19096 

or

$ awk '{$1=$1; print}' OFS=+ <<< '3829 4837 3729 2874 3827' | bc
19096

or

$ echo  '3829 4837 3729 2874 3827' |
    awk '{for (i=1; i<=NF; i++) c+=$i} {print c}'
19096

OTHER TIPS

If you have a list of numbers in a string, processing them as follows is the safest way to split and iterate (while avoiding glob expansion or other side effects):

max=0
read -r -a number_array <<<"$numbers"
for number in "${number_array[@]}"; do
  if (( number > max )) ; then
    max=number
  fi
done    

If you were reading from a file, a simple modification applies (see http://mywiki.wooledge.org/BashFAQ/001 for best practices on reading files):

max=0
while read -r -a numbers; do
  for number in "${numbers[@]}"; do
    if (( number > max )) ; then
      max=number
    fi
  done
done <input-file

If your delimiters are something else, you can set IFS appropriately. For instance, if delimited by commas:

while IFS=, read -r -a numbers; do

Note that the best practices given here don't matter as much when your values really are restricted to only ever be numbers. If you had a * in your input data, however, and you simply ran

for number in $numbers; do

then the * in the $numbers string would be replaced with a list of files in the current directory. Don't do that.

One final note -- bash has no built-in support for floating-point math. If you need floating-point math, see http://mywiki.wooledge.org/BashFAQ/022.

Here's a couple of ways to split a string of words into a structure you can iterate over:

use the positional parameters

numbers="3829 4837 3729 2874 3827"
set -- $numbers
sum=0
for n in "$@"; do
    # do something with $n
    (( sum += n ))
done
average=$(( sum / $# ))   # integer division only

use an actual array

numbers="3829 4837 3729 2874 3827"
nums=( $numbers )
sum=0
for n in "${nums[@]}"; do
    # do something with $n
    (( sum += n ))
done
average=$(( sum / ${#nums[@]} ))   # integer division only
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top