The main problem my code doesn't do echo every time(fibonacci sequence)
#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1 - 2]` + `fib $[$1 - 1]` ]
fi
}
fib $1
i was expecting it will do echo every time. It shows:
~/Bash$ ./fn.sh 12
144
but i need it to show like this:
~/Bash$ ./fn.sh 12
0
1
1
2
3
5
8
13
21
34
55
89
144
Your function is consuming the output of its invocation via backticks (command substitution). Only the last output is sent to the terminal. Your function will only return the n-th number of the Fibonacci sequence.
If you want to return all the numbers of the sequence up to a certain point, you can use a loop:
for i in $(seq "$i"); do
fib "$i"
done
Another method might be:
#!/bin/bash
fibseq () {
echo "$1"
if (($3 > 0)); then fibseq "$2" $(($1 + $2)) $(($3 - 1)); fi
}
if (($1 > 0)); then fibseq 0 1 "$1"; fi
Note that this is just a loop disguised as recursion. This method is much more efficient than the naive recursive version to compute Fibonacci sequences. Arguments to fibseq function: $3 serves as a counter, $1 and $2 are the last two Fibonacci numbers.
As an aside note, you can replace the $(($1 + $2)) with $(bc <<< "$1 + $2") if you want arbitrary-precision arithmetic.
Finally, don't use the $[…] form for arithmetic expansion. Use $((…)) instead.
Related
Is there a way to get rid of having to create 4 different do-while statements for each variable? For example, I would like to calculate 1!-7! w/o initializing 7 diff. variables and initializing 7 different variables. Is this possible?
Below is an example of how I would like the program to initiate and what the output should be
./filename.sh 1 2 3 4 5 6 7
The factorial 1! is 1
The factorial 2! is 2
The factorial 3! is 6
The factorial 4! is 24
…
Code:
#!/bin/bash
on1=0 #initiating the 4 variables
on2=0 #possible to compress these into 1 variable?
on3=0
on4=0
fact1=1
fact2=1
fact3=1
fact4=1
echo -n "Enter numbers to find factorial : " # gets user input of 4 #s
read n1 n2 n3 n4
on1=$n1
on2=$n2
on3=$n3
on4=$n4
while [ $n1 -ge 1 ] # calculates first factorial
do #possible to only make 1 loop?
fact1=`expr $fact1 \* $n1`
n1=`expr $n1 - 1`
done
while [ $n2 -ge 1 ] # calculates second factorial
do
fact2=`expr $fact2 \* $n2`
n2=`expr $n2 - 1`
done
while [ $n3 -ge 1 ] #calculates third factorial
do
fact3=`expr $fact3 \* $n3`
n3=`expr $n3 - 1`
done
while [ $n4 -ge 1 ] # calculates fourth factorial
do
fact4=`expr $fact4 \* $n4`
n4=`expr $n4 - 1`
done
echo "The factorial $on1! is $fact1" # outputs the four factorials
echo "The factorial $on2! is $fact2" #possible to make only 1 echo?
echo "The factorial $on3! is $fact3"
echo "The factorial $on4! is $fact4"
Use a loop to process each of the arguments.
#!/bin/bash
while [ $# -gt 0 ]
do
n=$1
on=$n
fact=1
while [ $n -ge 1 ]
do
fact=$(expr $fact \* $n)
n=$(expr $n - 1)
done
echo "The factorial of $on is $fact"
shift # go to the next argument
done
Just for fun, I tried a recursive solution:
#!/bin/zsh
function fact {
if (($1 < 2))
then
echo 1
else
expr $1 '*' $(fact $(($1-1)))
fi
}
for arg
do
fact $arg
done
I first thought, it would blow up soon due to stack overflow, but it calculated happily even pretty large values (such as 200!) in quite short time (in zsh; didn't try it with other shells)!
This question already has answers here:
Recursive Fibonacci in Bash script
(5 answers)
Closed 8 years ago.
I was perusing the sight for some help with my code and came across a thread from about 4 months ago, however the user's final revision doesn't work when posted into bash, and produces some strange results. Here is my version, which also produces some strange results:
#!/bin/bash
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 0
elif (( ind = 1 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
so this block of code will end up always outputting 1. ./fib.sh 5 outputs
fibbonacci sequence number 5 is 1
so I tried to write the code a little closer to what the previous asker had,
#!/bin/bash
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
While I don't understand the logic here, it actually starts to output fibonacci numbers,
but now I get a slightly different problem; ./fib.sh 3 outputs:
fibbonacci sequence number 3 is 5
./fib.sh 5 outputs :
fibbonacci sequence number 5 is 13
Well we know that the 3rd fibonacci number is 1, and the 5th is 3 so what gives? The code seems to skip ahead several fibonacci numbers, and I can't figure out what is logically wrong with my code.
Usually when writing Fibonacci sequence logic you have to special-case the first two numbers. That's what the first user has done: special-casing 0 and 1.
You've removed one instance of special-casing and shifted everything by one index, which explains one shift. The other is easy: the code is zero-indexed. That's why everything is "off by two".
What's wrong with the original code? This line:
elif ((ind = 1))
sets ind to 1. Otherwise it's fine.
A simple fix to your code is to replace this line:
if (( ind <= 0 ))
with
if (( ind <= 2 ))
and off you go. That gives you the one-indexed behavior you'd expect:
cternus#astarael ~/foo> for i in `seq 1 10`; do ./foo.sh $i; done
fibbonacci sequence number 1 is 1
fibbonacci sequence number 2 is 1
fibbonacci sequence number 3 is 2
fibbonacci sequence number 4 is 3
fibbonacci sequence number 5 is 5
fibbonacci sequence number 6 is 8
fibbonacci sequence number 7 is 13
fibbonacci sequence number 8 is 21
fibbonacci sequence number 9 is 34
fibbonacci sequence number 10 is 55
#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1-2]` + `fib $[$1 - 1]` ]
fi
}
fib $1
#!/bin/bash
#fibonacci sequence function
fib()
{
ind=$1
if (( ind <= 0 ))
then echo 0
elif (( ind == 2 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
So my problem was with the equality check in
elif (( ind = 1 ))
I should have used the double =, and change the 1 to a 2, so it should have been
elif (( ind == 2 ))
Ultimately my correct script should look like this
#!/bin/bash
#fibonacci sequence function
fib()
{
ind=$1
if (( ind <= 1 ))
then echo 0
elif (( ind == 2 ))
then echo 1
else
echo $(( $(fib $((ind - 1)) ) + $(fib $((ind - 2)) ) ))
fi
}
echo fibbonacci sequence number $1 is $(fib $1)
Thanks a bajillion and one to Christian Ternus for the help, I've been programming for a few years now and totally should have seen the equality check error >.<
Interestingly enough, the Korn shell executes #christianternus' script much faster than other shells:
$ for a in sh bash zsh ksh; do echo "shell: $a"; time for i in $(seq 1 20); do $a bin/fib.sh $i; done | md5sum; done
shell: sh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.508s
user 0m3.788s
sys 0m11.785s
shell: bash
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.906s
user 0m3.604s
sys 0m11.235s
shell: zsh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m29.203s
user 0m2.505s
sys 0m14.377s
shell: ksh
5fece53a38f2df040bfaf9632c2b7f4b -
real 0m0.942s
user 0m0.843s
sys 0m0.079s
This is my attempt:
#!/bin/bash
function fibonacci(){
first=$1
second=$2
if (( first <= second ))
then
return 1
else
return $(fibonacci $((first-1)) ) + $(fibonacci $((second-2)) )
fi
}
echo $(fibonacci 2 0)
I think i'm having trouble with the else statement. I get the error return: +: numeric argument required
on line 14.
The other problem that i'm having is that the script doesn't display any numbers even if i do echo $(fibonacci 0 2). I thought it would display a 1 since i'm returning a 1 in that case. Can someone give me a couple of tips on how to accomplish this?
After checking out some of your answers this is my second attempt.. It works correctly except that it displays the nth fibonacci number in the form 1+1+1+1 etc. Any ideas why?
#!/bin/bash
function fibonacci(){
first=$1
second=$2
if (( first <= second ))
then
echo 1
else
echo $(fibonacci $((first-1)) ) + $(fibonacci $((first-2)) )
fi
}
val=$(fibonacci 3 0)
echo $val
My final attempt:
#!/bin/bash
function fibonacci(){
first=$1
if (( first <= 0 ))
then
echo 1
else
echo $(( $(fibonacci $((first-1)) ) + $(fibonacci $((first-2)) ) ))
fi
}
val=$(fibonacci 5)
echo $val
thanks dudes.
#!/bin/bash
function fib(){
if [ $1 -le 0 ]; then
echo 0
elif [ $1 -eq 1 ]; then
echo 1
else
echo $[`fib $[$1-2]` + `fib $[$1 - 1]` ]
fi
}
fib $1
The $(...) substitution operator is replaced with the output of the command. Your function doesn't produce any output, so $(...) is the empty string.
Return values of a function go into $? just like exit codes of an external command.
So you need to either produce some output (make the function echo its result instead of returning it) or use $? after each call to get the value. I'd pick the echo.
As Wumpus said you need to produce output using for example echo.
However you also need to fix the recursive invocation.
The outermost operation would be an addition, that is you want:
echo $(( a + b ))
Both a and b are substitutions of fibonacci, so
echo $(( $(fibonacci x) + $(fibonacci y) ))
x and y are in turn arithmetic expressions, so each needs its own $(( )), giving:
echo $(( $(fibonacci $((first-1)) ) + $(fibonacci $((second-2)) ) ))
If you are confused by this, you should put the components into temporary variables and break down the expression into parts.
As to the actual fibonacci, it's not clear why you are passing 2 arguments.
Short version, recursive
fib(){(($1<2))&&echo $1||echo $(($(fib $(($1-1)))+$(fib $(($1-2)))));}
While calculating fibonacci numbers with recursion is certainly possible, it has a terrible performance. A real bad example of the use of recursion: For every (sub) fibonacci number, two more fibonacci numbers must be calculated.
A much faster, and simple approach uses iteration, as can be found here:
https://stackoverflow.com/a/56136909/1516636
I tried to create a shell script, which sum the given numbers. If there is no given parameter, then it tries to read the pipe output, but I get an error.
#!/bin/sh
sum=0
if [ $# -eq 0 ]
then
while read data
do
sum=`expr $sum + $data`
done
else
for (( i = 1 ; i <= $#; i++ ))
do
sum=`expr $sum + ${!i}`
done
fi
echo $sum
This works: sum 10 12 13
But this one doesn't: echo 10 12 13| sum
Thanks in advance,
Here you go (assuming bash, not sh):
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Convert this array into a string s, with elements separated by a +
printf -v s "%s+" ${data[#]}
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to sum
(( sum += s ))
done
else
# If elements come from argument line, do the same!
printf -v s "%s+" $#
# Append 0 to s (observe that s ended with a +)
s="${s}0"
# Add these numbers to obtain sum
(( sum = s ))
fi
echo $sum
You can invoke it thus:
$ echo 10 12 13 | ./sum
35
$ ./sum 10 12 13
35
$ # With several lines and possibly empty lines:
$ { echo 10 12 13; echo; echo 42 22; } | ./sum
99
Hope this helps!
Edit. You might also be interested in learning cool stuff about IFS. I've noticed that people tend to confuse # and * in bash. If you don't know what I'm talking about, then you should use # instead of *, also for array subscripts! In the bash manual, you'll find that when double quoted, $* (or ${array[*]}) expands to all the elements of the array separated by the value of the IFS. This can be useful in our case:
#!/bin/bash
sum=0
if (( $# == 0 )); then
# Read line by line
# But each line might consist of separate numbers to be added
# So read each line as an array!
while read -a data; do
# Now data is an array... but if empty, continue
(( ${#data[#]} )) || continue
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( sum + ${data[*]} ))
done
else
# If elements come from argument line, do the same!
# Setting IFS=+ (just for the sum) will yield exactly what I want!
IFS=+ sum=$(( $* ))
fi
echo $sum
Gniourf now exits from teacher mode. :-)
I have a problem with writing it in bash... I know how it works in C++, but I have trouble implementing it in bash. Here's what I got:
sum()
{
let minusOne=$1-1
let result=sum $minusOne +$1
}
You need an exit condition. In bash, $((...)) is arithmetic expansion, and $(...) is command substitution (see the man page).
sum() {
if (( $1 == 1 )); then
echo 1
return
fi
local minusOne=$(( $1 - 1 ))
echo $(( $1 + $(sum $minusOne) ))
}
A non-recursive way to write a sum function:
sum() {
set -- $(seq 1 $1)
local IFS=+
echo "$*" | bc
}
Here is a function that will give you a sum of numbers provided as arguments. The following prints "10":
#!/bin/bash
sum() {
local total=0
for number in "$#"; do
(( total += number ))
done
echo $total
}
sum 1 2 3 4