Recursive function in bash - bash

I want to do a function that will return the factorial of a number in bash
Here's the current code that doesn't work, can anyone tell me what's wrong and how to correct it? I just started learning bash and I don't know that much.
#!/bash/bin
factorial()
{
let n=$1
if (( "$n" <= "1" ))
then return 1
else
factorial n-1
return $n*$?
fi
return 0
}
factorial 5
echo "factorial 5 = $?"

There are several syntax and a quite obvious logic one (return 0)
A working version is below:
#!/bin/bash
factorial()
{
if (( $1 <= 1 )); then
echo 1
else
last=$(factorial $(( $1 - 1 )))
echo $(( $1 * last ))
fi
}
factorial 5
You are missing:
return is bad (should use echo)
shbang line (is /bin/bash not /bash/bin)
Can't do arithmetic outside of (( )) or $(( )) (or let, but (( )) is preferred)

#!/bin/bash
function factorial()
{
if (( $1 < 2 ))
then
echo 1
else
echo $(( $1 * $(factorial $(( $1 - 1 ))) ))
fi
}
This will work better.
(It works up to 25, anyway, which should be enough to prove the point about recursion.)
For higher numbers, bc would be the tool to use, making the ninth line above:
echo "$1 * $(factorial $(( $1 - 1 )))" | bc
but you have to be a bit careful with bc --
$ factorial 260
38301958608361692351174979856044918752795567523090969601913008174806\
51475135399533485285838275429773913773383359294010103333339344249624\
06009974551133984962615380298039823284896547262282019684886083204957\
95233137023276627601257325925519566220247124751398891221069403193240\
41688318583612166708334763727216738353107304842707002261430265483385\
20637683911007815690066342722080690052836580858013635214371395680329\
58941156051513954932674117091883540235576934400000000000000000000000\
00000000000000000000000000000000000000000
was quite a strain on my poor system!

echo-ing a result may be the only way to get a result for n > 5, but capturing the echo'ed result requires a subshell, which means the recursion will get expensive fast. A cheaper solution is to use a variable:
factorial() {
local -i val=${val:-($1)}
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
factorial $(( $1 - 1 ))
}
If you want to be extra sure that val is unset when you start, use a wrapping function:
factorial() {
local -i val=$1
_fact() {
if (( $1 <= 1 )); then
echo $val
return
fi
(( val *= $1 - 1 ))
_fact $(( $1 - 1 ))
}
_fact $1
}
For comparison:
# My Method
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.028s
user 0m0.026s
sys 0m0.001s
# A capturing-expression solution
$ time for i in {0..100}; do factorial $(( RANDOM % 21 )); done > /dev/null
real 0m0.652s
user 0m0.221s
sys 0m0.400s

clear cat
fact()
{
i=$1
if [ $i -eq 0 -o $i -eq 1 ]
then
echo 1
else
f=`expr $i \- 1`
f=$(fact $f)
f=`expr $i \* $f`
echo $f
fi
}
read -p "Enter the number : " n
if [ $n -lt 0 ]
then
echo "ERROR"
else
echo "THE FACTORIAL OF $n : $(fact $n) "
fi

Another one implementation using echo instead of return
#!/bin/bash
factorial()
{
if [ $1 -le 1 ]
then
echo 1
else
echo $[ $1 * `factorial $[$1-1]` ]
fi
}
echo "factorial $1 = " `factorial $1`

#-----------------factorial ------------------------
# using eval to avoid process creation
fac=25
factorial()
{
if [[ $1 -le 1 ]]
then
eval $2=1
else
factorial $[$1-1] $2
typeset f2
eval f2=\$$2
((f2=f2*$1))
eval $2=$f2
fi
}
time factorial $fac res
echo "factorial =$res"

Compute factor recursively under bash
In addition to all answer, I would like to suggest:
Store computed factorials in order to avoid re-compute
Using an array to store computed factorials, could improve a lot your function, if you plan to run this function many times!!
For this, we need to reverse recursion way, in order to store each computed factorial:
declare -ia _factorials=(1 1)
factorial() {
local -i _req=$1 _crt=${2:-${#_factorials[#]}} _next=_crt+1 \
_factor=${3:-${_factorials[#]: -1}}
if [ "${_factorials[_req]}" ]; then
printf %u\\n ${_factorials[_req]}
else
printf -v _factorials[_crt] %u $((_factor*_crt))
factorial $1 $_next ${_factorials[_next]}
fi
}
Then
factorial 5
120
Ok, and:
declare -p _factorials
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120")
Then if I do set -x to trace operations and ask for higher value:
set -x
factorial 10
+ factorial 10
+ local -i _req=10 _crt=6 _next=_crt+1 _factor=120
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 720
+ factorial 10 7
+ local -i _req=10 _crt=7 _next=_crt+1 _factor=720
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 5040
+ factorial 10 8
+ local -i _req=10 _crt=8 _next=_crt+1 _factor=5040
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 40320
+ factorial 10 9
+ local -i _req=10 _crt=9 _next=_crt+1 _factor=40320
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 362880
+ factorial 10 10
+ local -i _req=10 _crt=10 _next=_crt+1 _factor=362880
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 3628800
+ factorial 10 11
+ local -i _req=10 _crt=11 _next=_crt+1 _factor=3628800
+ '[' 3628800 ']'
+ printf '%u\n' 3628800
3628800
Where recursions was done from 5 to 10, and factorial 5 was directly used.
declare -p _factorials
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120"
[6]="720" [7]="5040" [8]="40320" [9]="362880" [10]="3628800")
Avoid forks: replace echo by setting a variable:
declare -ia _factorials=(1 1)
factorialtovar() {
local -i _req=$2 _crt=${3:-${#_factorials[#]}} _next=_crt+1 \
_factor=${4:-${_factorials[#]: -1}}
if [ "${_factorials[_req]}" ]; then
printf -v $1 %u ${_factorials[_req]}
else
printf -v _factorials[_crt] %u $((_factor*_crt))
factorialtovar $1 $2 $_next ${_factorials[_next]}
fi
}
Then
factorialtovar myvar 7 ; echo $myvar
5040
declare -p myvar _factorials
declare -- myvar="5040"
declare -ai _factorials=([0]="1" [1]="1" [2]="2" [3]="6" [4]="24" [5]="120" [6]="720" [7]="5040")
and
set -x
factorialtovar anothervar 10
+ factorialtovar anothervar 10
+ local -i _req=10 _crt=8 _next=_crt+1 _factor=5040
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 40320
+ factorialtovar anothervar 10 9
+ local -i _req=10 _crt=9 _next=_crt+1 _factor=40320
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 362880
+ factorialtovar anothervar 10 10
+ local -i _req=10 _crt=10 _next=_crt+1 _factor=362880
+ '[' '' ']'
+ printf -v '_factorials[_crt]' %u 3628800
+ factorialtovar anothervar 10 11
+ local -i _req=10 _crt=11 _next=_crt+1 _factor=3628800
+ '[' 3628800 ']'
+ printf -v anothervar %u 3628800
set +x
+ set +x
echo $anothervar
3628800

To complete #Marc Dechico solution, instead of eval, passing reference is preferable nowadays (bash >= 4.3) :
factorial_bruno() {
local -i val="$1"
local -n var="$2" # $2 reference
_fact() {
if (( $1 <= 1 )); then
var="$val"
return
fi
((val*=$1-1))
_fact $(($1-1))
}
_fact "$1"
}
declare -i res
factorial_bruno 20 res
printf "res=%d\n" "$res"
If we compare the time on a 1,000 runs of #kojiro's, #techno's (catching result for them, as after all we want the result), #Marc Dechici's, and my solution, we get :
declare -i res
TIMEFORMAT=$'\t%R elapsed, %U user, %S sys'
echo "Kojiro (not catching result) :"
time for i in {1..1000}; do factorial_kojiro $((i%21)); done >/dev/null
echo "Kojiro (catching result) :"
time for i in {1..1000}; do res=$(factorial_kojiro $((i%21))); done
echo "Techno (not catching result) :"
time for i in {1..1000}; do factorial_techno $((i%21)); done >/dev/null
echo "Techno (catching result, 100% data already cached) :"
time for i in {1..1000}; do res=$(factorial_techno $((i%21))); done
_factorials=(1 1)
echo "Techno (catching result, after cache reset) :"
time for i in {1..1000}; do res=$(factorial_techno $((i%21))); done
echo "Marc Dechico :"
time for i in {1..1000}; do factorial_marc $((i%21)) res; done
echo "This solution :"
time for i in {1..1000}; do factorial_bruno $((i%21)) res; done
Kojiro (not catching result) :
0.182 elapsed, 0.182 user, 0.000 sys
Kojiro (catching result) :
1.510 elapsed, 0.973 user, 0.635 sys
Techno (not catching result) :
0.054 elapsed, 0.049 user, 0.004 sys
Techno (catching result, 100% data already cached) :
0.838 elapsed, 0.573 user, 0.330 sys
Techno (catching result, after cache reset) :
2.421 elapsed, 1.658 user, 0.870 sys
Marc Dechico :
0.349 elapsed, 0.348 user, 0.000 sys
This solution :
0.161 elapsed, 0.161 user, 0.000 sys
It is interesting to notice that doing an output (echo/printf) in a function and catching the result with res=$(func...) (subshell) is always very expensive, 80% of the time for Kojiro's solution, >95% for Techno one...
EDIT: By adding a cache with similar solution (#techno new solution - the difference is the use of printf -v instead of using variable reference), we can further improve response time if we need to calculate many times the factorial. With integer limit within bash, that would mean we need to calculate many times the same factorial (probably useful for benchmarks like this one :-)

Avoiding forks!
The only answer here addressing correctly the process thread is kojiro's answer, but I dislike using a dedicated variable for this.
For this, we could use arguments to transmit steps or intermediary results:
factorial() {
if [ $1 -gt 1 ]; then
factorial $(( $1 - 1 )) $(( ${2:-1} * $1 ))
else
echo $2
fi
}
There is no forks and result is passed as 2nd argument.
Avoiding fork (bis)
Then in order to avoid forks, I prefer storing result into a variable instead of using echo (or other printf):
setvarfactorial() {
if [ $2 -gt 1 ]; then
setvarfactorial $1 $(( $2 - 1 )) $(( ${3:-1} * $2 ))
else
printf -v "$1" %u $3
fi
}
Usage:
factorial 5
120
setvarfactorial result 5
echo $result
120
When goal is to store result into a variable, this second method is a lot more system friendly and quicker than using
result=$(factorial 5)
# or same
result=`factorial 5`
Comparison between with or without forks.
There is no variable set, just running 10x factorial 20:
Tested on my old raspberry-pi:
time for i in {1..10};do factorial 20;done|uniq -c
10 2432902008176640000
real 0m0.376s
user 0m0.159s
sys 0m0.035s
time for i in {1..10};do kojirofactorial 20;done|uniq -c
10 2432902008176640000
real 0m0.348s
user 0m0.150s
sys 0m0.023s
time for i in {1..10};do danielfactorial 20;done|uniq -c
10 2432902008176640000
real 0m5.859s
user 0m1.015s
sys 0m1.732s
If kojiro's version seem a little quicker than mine, difference doesn't really matter, but other answer will take more than 10x more time!!
To see of forks are done:
set -x
danielfactorial 5
+ danielfactorial 5
+ (( 5 <= 1 ))
++ factorial 4
++ (( 4 <= 1 ))
+++ factorial 3
+++ (( 3 <= 1 ))
++++ factorial 2
++++ (( 2 <= 1 ))
+++++ factorial 1
+++++ (( 1 <= 1 ))
+++++ echo 1
++++ last=1
++++ echo 2
+++ last=2
+++ echo 6
++ last=6
++ echo 24
+ last=24
+ echo 120
120
vs:
factorial 5
+ factorial 5
+ '[' 5 -gt 1 ']'
+ factorial 4 5
+ '[' 4 -gt 1 ']'
+ factorial 3 20
+ '[' 3 -gt 1 ']'
+ factorial 2 60
+ '[' 2 -gt 1 ']'
+ factorial 1 120
+ '[' 1 -gt 1 ']'
+ echo 120
120

#!/bin/bash
fact() {
(( $1 == 0 )) && echo 1 || echo "$(( $1 * $(fact $(($1-1)) ) ))"
}
fact $1

Related

Print Fibonacci series using recursion in bash with only 1 variable

I'd like to know how to print Fibonacci series using recursion in bash with only 1 variable.
From what I've done:
fib()
{
i=$1
if (( $i <= 1 ))
then echo 0
elif (( $i == 2 ))
then echo 1
else
echo $(( $(fib $(($i - 1)) ) + $(fib $(($i - 2)) ) ))
fi
}
echo $(fib $1)
I get the correct output of the final iteration, for example if I enter 10 I will get 34, but I'd like to print the whole sequence of numbers, i.e. all the iterations as well. How can I achieve that?
Another way I tried was by:
#!/bin/bash
arr[0]=0
arr[1]=1
for (( i=0; i<=10; i++ ))
do
echo -n "${arr[0]} "
arr[0]=$((${arr[0]} + ${arr[1]} ))
arr[1]=$((${arr[0]} - ${arr[1]} ))
done
echo ""
But obviously here I've used a for loop, but I don't want to use another variable.
Just for (my kind of) fun, this code prints the Fibonacci numbers from the 0th to the 92nd (as defined in Fibonacci number - Wikipedia) with a recursive function that uses no variables:
#! /bin/bash
function fib
{
echo ${3-0}
(($1 > 0)) && fib $(($1-1)) ${3-0} $((${2-1}+${3-0}))
}
fib 92
Some may claim that using the positional parameters ($1, $2, $3) for this is cheating, but then other solutions could be said to be using two variables ($i and $1).
The code takes under 0.01 seconds to run on my (oldish) Linux machine.
The code should work with numbers up to 92 with Bash version 3 or later on any platform. See Bash Number Limit?. Numbers higher than 93 will cause to code to produce garbage results due to arithmetic overflow.
Variables in bash are global by default. You need to make i local explicitly.
fib () {
local i
i=$1
if (( i <= 1 )); then
echo $i
else
echo $(( $(fib $((i-1)) ) + $(fib $((i - 2)) ) ))
fi
}
(Also, your base cases are a little off if you are starting with 0, and 2 need not be a base case; fib 2 can be derived from the base cases fib 0 and fib 1.)
If you want to print each fibonacci value from 1 to $n, I suggest:
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif (( i <= 1 )); then
echo $i
else
echo $(( $($FUNCNAME $((i - 1)) ) + $($FUNCNAME $((i - 2)) ) ))
fi
}
fib() {
local i
for (( i = 1; i <= $1; i++ )); do
fib_r $i
done
}
fib 10
outputs
0
1
1
2
3
5
8
13
21
34
It's still one variable, albeit one per function.
I use the bash variable $FUNCNAME in the recursive function so you don't have to hardcode the function name within itself. I got bit by not updating that line when I renamed the function.
Of course your performance will greatly improve if you cache the results: "fib 16" takes, on my VM, about 3.5 sec without caching and about 0.03 sec with caching.
fib_r() {
local i=$1
if (( i < 0 )); then
echo "Error: negative numbers not allowed" >&2
exit 1
elif [[ -n ${fib_cache[i]} ]]; then
echo "${fib_cache[i]}"
elif (( i <= 1 )); then
echo $i
else
echo $(( $( $FUNCNAME $((i - 1)) ) + $( $FUNCNAME $((i - 2)) ) ))
fi
}
fib_cache=()
fib() {
local i
for ((i=1; i<=$1; i++)); do
fib_cache[i]=$(fib_r $i)
echo "${fib_cache[i]}"
done
}

Quicksort implemented in shell script doesn't work

I am learning shell scripting and trying to implement quick sort using it.
But it doesn't work, actually it acting weird.
The script:
#!/bin/bash
declare -a data=()
declare -r size=10
declare -i steps=0
for i in $(seq 0 $size); do
data[$i]=$(expr $RANDOM % $size)
done
function partition() {
pivot=${data[$1]}
left=$(expr $1 + 1)
right=$2
while true; do
while [[ $left -le $right && ${data[$left]} -le $pivot ]]; do
left=$(expr $left + 1)
steps=$(expr $steps + 1)
done
while [[ $right -ge $left && ${data[$right]} -ge $pivot ]]; do
right=$(expr $right - 1)
steps=$(expr $steps + 1)
done
if [[ $left -gt $right ]]; then
break
fi
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
echo $right
}
function quickSort() {
if [[ $1 -lt $2 ]]; then
local partitionPoint=$(partition $1 $2)
quickSort $1 $(expr $partitionPoint - 1)
quickSort $(expr $partitionPoint + 1) $2
fi
}
# involve the algorithm
quickSort 0 $(expr $size - 1)
echo "Steps: $steps"
echo ${data[#]}
I tried to log some variable but it's just weird I can't figure out what's going on.
When I comment out all the code in the two functions and 'manually' update elements of data variable, it did changed.
I tried to log some variables and they all changing.
But the final output remains untouched.
Or maybe it eventually reversed all the flipping but I don't know.
I can't figure it out.
At last I compare my python implementation line by line. No mistakes. But it just not working.
Am I miss something?
Variable scope or something?
Any advice will be appreciated.
There are several smaller issues in this code, but the biggest issue is here:
partitionPoint=$(partition $1 $2)
This is problematic because $( ... ) runs ... in a subshell -- a separate, fork()ed-off process, consequently with its own variable scope.
If you instead return your result via indirect assignment, making it:
partition "$1" "$2" partitionPoint
and inside the function using:
printf -v "$3" %s "$right"
...to assign the value to the variable so named, things work much better.
#!/bin/bash
PS4=':$LINENO+'; set -x
data=()
size=10
steps=0
for ((i=0; i<size; i++)); do
data[$i]=$((RANDOM % size))
done
partition() {
local pivot left right dest temp
pivot=${data[$1]}
left=$(($1 + 1))
right=$2
dest=$3
while true; do
while (( left <= right )) && (( ${data[$left]} <= pivot )); do
left=$(( left + 1 ))
steps=$(( steps + 1 ))
done
while (( right >= left )) && (( ${data[$right]} >= pivot )); do
right=$(( right - 1 ))
steps=$(( steps + 1 ))
done
(( left > right )) && break
temp=${data[$left]}
data[$left]=${data[$right]}
data[$right]=$temp
done
: '$1='"$1" right="$right" 'data[$1]='"${data[$1]}" 'data[$right]='"${data[$right]}"
temp=${data[$1]}
data[$1]=${data[$right]}
data[$right]=$temp
printf -v "$dest" %s "$right"
}
quickSort() {
local partitionPoint
if (( $1 < $2 )); then
partition "$1" "$2" partitionPoint
quickSort "$1" "$(( partitionPoint - 1 ))"
quickSort "$((partitionPoint + 1))" "$2"
fi
}
# involve the algorithm
quickSort 0 "$(( size - 1 ))"
echo "Steps: $steps"
printf '%s\n' "${data[#]}"

How to stop bash from creating subshells when recursively call a function

This is a simple shell function to calculate factorial.
#!/bin/bash
function factorial()
{
if (( $1 < 2 ))
then
echo 1
else
echo $(( $1 * $(factorial $(( $1 - 1 ))) ))
fi
}
factorial $1
But I find that this script will create many sub-shells when given a very big input. It is not necessary and not efficient. Is there any way to call recursive functions without creating new sub-shells?
My goal is not how to write a factorial function in shell, but how to avoid creating sub-shells when call recursively defined functions.
In fact, even a simple function call will cause creation of sub-shells:
#!/bin/bash
function fac0() {
ps >> log
echo $1
}
function fac1() {
ps >> log
echo $(( $1 * $(fac0 $(( $1 - 1 ))) ))
}
touch log
fac1 $1
After run the script, the log file's content is: (It still creates sub-shells)
PID TTY TIME CMD
9205 pts/8 00:00:00 bash
9245 pts/8 00:00:00 call_subshell.s
9247 pts/8 00:00:00 ps
PID TTY TIME CMD
9205 pts/8 00:00:00 bash
9245 pts/8 00:00:00 call_subshell.s
9248 pts/8 00:00:00 call_subshell.s
9249 pts/8 00:00:00 ps
Because of sub-shell creation, other unwanted aspects exist.
#!/bin/bash
declare -i i
i=0
function factorial() {
i=$(($i + 1))
if (( $1 < 2 ))
then
echo 1
else
local c=$(( $1 - 1 ))
echo $(( $1 * $(factorial $c) ))
fi
}
factorial $1
echo $i
This script will print $i=1 no matter what number the argument is!
Avoid the recursion:
#!/bin/bash
factorial() {
local f=1
for ((i=2; i<=$1; i++)) ; do
(( f *= i ))
done
echo $f
}
factorial $1
The subshell is spawned because of command substitution. Use a "argument as a result" way to avoid it:
#!/bin/bash
# set -xv
factorial() {
# echo $BASH_SUBSHELL
if [[ $2 ]] ; then
if (( $2 == 1 )) ; then
echo $1
else
factorial $(( $1 * $2 )) $(( $2 - 1 ))
fi
else
factorial $1 $(( $1 - 1 ))
fi
}
factorial $1
One need only separate the calculation from the function call and use the 'let' builtin. If there were commands in the parameters, use the 'eval' builtin rather than $(). Not as elegant, perhaps, but avoids subshells.
if [[ $2 ]] ; then
if (( $2 == 1 )) ; then
echo $1
else
let "p1=$1*$2"
let "p2 = $2-1"
factorial $p1 $p2
fi
else
let "p2 = $1-1"
factorial $1 $p2
fi

Bourne shell: reading multiple lines of stdin

I am new to Bourne shells and was curious about reading stdin on multiple lines/that includes newline.
IN: For example, if someone types in
1
3 #only ignore whitespace
5.3 #ignore floating point number all together
I could have it read them and echo them on separate lines. My goal is to also ignore floating point numbers (integers only!) and ignore whitespace.
OUT: My ideal output would calculate the average (example with above input)
1
2
So far I can read stdin and echo it back if it is on one line and limited to a hardcoded value (should be whenever user stops with Ctrl-D).
#! /bin/sh
read a b c
echo "$a";
echo "$b";
echo "$c";
It is been a while, but the following should obtain a sum and average for only integer input:
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
;;
esac
done
printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n", "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n 3\n5.3\n" | sh bourne_sum.sh
The sum is '4', cnt is '2' the average is '2'
Input in a File
$ cat dat/bsum.txt
1
3
5.3
Simple redirection will do:
$ sh bourne_sum.sh <dat/bsum.txt
The sum is '4', cnt is '2' the average is '2'
Modified to output on 1 line
#!/bin/sh
sum=0;
cnt=0
while read line
do
case "$line" in
*[.]* )
printf " 0"
continue
;;
[0-9]* )
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
;;
esac
done
printf "\n"
# printf "\nThe sum is '%s', cnt is '%s' the average is '%s'\n\n" "$sum" "$cnt" `expr $sum / $cnt`
Output
$ printf "1\n3\n5\n4.4\n" | sh bourne_sum.sh
1 2 3 0
Responding to ONLY Integers
#!/bin/sh
sum=0;
cnt=0
while read line
do
if [ $line -eq $line 2>/dev/null ]
then
sum=`expr "$sum" + "$line"`
cnt=`expr "$cnt" + 1`
printf " %s" `expr $sum / $cnt`
fi
done
printf "\n"
Output
$ printf "1\n3\n5\n4.4\n'2 3'\n" | sh bourne_sum.sh
1 2 3

how to extract numbers from this echo into separate variables?

Sorry about bits and snippit of information
So I am writing an average shell script program
so if use inputs
echo 1 3, .... | sh get_number
I would have to pull the numbers seperated by spaces from echo to be
var1 = 1, var2= 3, etc.
I tried
#!/bin/sh
sum=0
for i in $*
do
sum=`expr $sum + $i`
done
avg=`expr $sum / $n`
echo Average=$avg
but doesnt work....
do I include a read here?
also how would I do
sh get_number <file1>, <file2>... to grab numbers in them and sum them
in shell script?
Thanks
Sounds like you are looking for the read shell builtin:
% echo "1 2 3 4" | read a b stuff
% echo $b
2
% echo $stuff
3 4
To fix up your code:
for i in $*; do
sum=$(( sum + i ))
n=$(( n + 1 ))
done
echo "Average=$(( sum / n ))"
#!/bin/sh
while [ $# -gt 0 ]; do
(( i++ ))
(( sum += $1 ))
shift
done
echo "Average=$(( sum/i ))"
Note: This fails in dash which is the closest shell I could find to a real sh.
An example of reading values from files passed as command line arguments or from lines read from stdin:
add_to_sum() {
set $*
while [ $# -gt 0 ]; do
I=`expr $I + 1`
SUM=`expr $SUM + $1`
shift
done
}
I=0
SUM=0
if [ $# -gt 0 ]; then
# process any arguments on the command line
while [ $# -gt 0 ]; do
FILE=$1
shift
while read LINE; do
add_to_sum "$LINE"
done < "$FILE"
done
else
# if no arguments on the command line, read from stdin
while read LINE; do
add_to_sum "$LINE"
done
fi
# be sure not to divide by zero
[ $I -gt 0 ] && echo Average=`expr $SUM / $I`

Resources