Why does this bash script work even with the typo - bash

So I have this bash script to find the Fibonacci sequence for n terms. Its definitely not great code, but I notices that it still works with a typo
read -p "Enter which term of fibo seq to find: " n
table=(1 1)
for i in $(seq 0 $(($n - 1)) )
do
if [[ -z ${table[$i]} ]]; then
a1=$(( $i - 1 ))
a2=$(( $I - 2 ))
val=$(( ${table[$a1]} + ${table[$a2]} ))
table+=($val)
fi
done
echo ${table[#]}
inside the if statement evaluating the expression for a2, there is a capital 'I' instead of 'i'. My understanding is that shellscript is case sensitive but I'm new to it. So can anyone explain why this works?

a2 will always be -2. Starting with bash 4.2, you can index into an array using negative indices, much like in Python. So with or without the typo, you are always accessing the second last element in the array.

The statement a2=$(( $I - 2 )) where $I is not declared is equal to a2=$(( - 2 )) which means a2 will always be equal to -2.

Related

How do I write a Bash script for a C program that takes 4 numerical inputs? [duplicate]

This question already has answers here:
How to zero pad a sequence of integers in bash so that all have the same width?
(15 answers)
Closed 2 years ago.
I am writing a bash script for a c program, where the program asks for a 4 numerical pin inputs. However, when I wrote the script, the output seems to run in a loop, but it doesn't break where it gets identified as the correct number the program will accept.
#!/bin/bash
RANGE=9000
count=${RANDOM:0:4}
while [[ "$count" -le $RANGE ]]
do
number=$RANDOM
(( "number %= $RANGE" ))
echo $number
if [[ "$count" == "$RANGE" ]]; then
break
fi
done
When I run it, I can see some numbers in the output that returned as 2 or 3 digits, instead of 4. So in theory, what I want to do is find a random number that is 4 digits that the program will take, but I don't know what is the random number, so essentially it is a brute force, or just me manually guessing the pin number.
If all you need is a random 4-digit number, you can do that with:
printf -v number "%04d" $((RANDOM % 10000))
The $RANDOM gives you a random number 0..32767, the % 10000 translates that to the range 0..9999 (not perfectly distributed, but should be good enough for most purposes), and the printf ensures leading zeros are attached to it (so you'll see 0042 rather than 42, for example).
You can test it with the following script:
(( total = 0 ))
(( bad = 0 ))
for i in {1..10000} ; do
printf -v x "%04d" $((RANDOM % 10000))
(( total += 1 ))
[[ "${x}" =~ ^[0-9]{4}$ ]] || { echo Bad ${x}; (( bad += 1 )); }
done
(( good = total - bad ))
echo "Tested: ${total}, bad ${bad}, good ${good}"
which should give you:
Tested: 10000, bad 0, good 10000

Adding a list of space separated numbers

Currently stuck in a situation where I ask the user to input a line of numbers with a space in between, then have the program display those numbers with a delay, then add them. I have everything down, but can't seem to figure out a line of code to coherently calculate the sum of their input, as most of my attempts end up with an error, or have the final number multiplied by the 2nd one (not even sure how?). Any help is appreciated.
echo Enter a line of numbers to be added.
read NUMBERS
COUNTER=0
for NUM in $NUMBERS
do
sleep 1
COUNTER=`expr $COUNTER + 1`
if [ "$NUM" ]; then
echo "$NUM"
fi
done
I've tried echo expr $NUM + $NUM to little success, but this is really all I can some up with.
Start with
NUMBERS="4 3 2 6 5 1"
echo $NUMBERS
Your script can be changed into
sum=0
for NUM in ${NUMBERS}
do
sleep 1
((counter++))
(( sum += NUM ))
echo "Digit ${counter}: Sum=$sum"
done
echo Sum=$sum
Another way is using bc, usefull for input like 1.6 2.3
sed 's/ /+/g' <<< "${NUMBERS}" | bc
Set two variables n and m, store their sum in $x, print it:
n=5 m=7 x=$((n + m)) ; echo $x
Output:
12
The above syntax is POSIX compatible, (i.e. works in dash, ksh, bash, etc.); from man dash:
Arithmetic Expansion
Arithmetic expansion provides a mechanism for evaluating an arithmetic
expression and substituting its value. The format for arithmetic expan‐
sion is as follows:
$((expression))
The expression is treated as if it were in double-quotes, except that a
double-quote inside the expression is not treated specially. The shell
expands all tokens in the expression for parameter expansion, command
substitution, and quote removal.
Next, the shell treats this as an arithmetic expression and substitutes
the value of the expression.
Two one-liners that do most of the job in the OP:
POSIX:
while read x ; do echo $(( $(echo $x | tr ' ' '+') )) ; done
bash:
while read x ; do echo $(( ${x// /+} )) ; done
bash with calc, (allows summing real, rational & complex numbers, as well as sub-operations):
while read x ; do calc -- ${x// /+} ; done
Example input line, followed by output:
-8!^(1/3) 2^63 -1
9223372036854775772.7095244707464171953

bash - while loop - rolling 2 dice

I'm writing a bash script that rolls 2 dice(with 6 sides). When the 2 dice hits double sixes I want the script to stop (break) and count how many rolls it took to get double sixes.
#!/bin/bash
DOUBLESIX="6-6"
while (( 0 ==0 )) ; do
dice=$RANDOM; ((dice = dice % 6 )); (( dice = dice +1 ))
dice2=$RANDOM; ((dice2 = dice2 % 6 )); (( dice = dice + 1))
pair="$dice-dice$2"
echo $pair
if [[ "$pair" == "$DOUBLESIX" ]]; then
break
fi
done
echo "It took $count rolls to get 6-6 "
Here's what i have so far. Question is, how do I count how many times the while loop ran and put it in my $count?
Thanks in advance!
I won't comment too much on the other potential issues you have with your code, such as the dice$2 "variable", or the fact you can generate a random number between one and six inclusive with the somewhat simpler ((num = $RANDOM % 6 + 1)) - the learning process of fixing/improving those is what will make you a better coder.
But, for the specific question on how to maintain a count, that's relatively simple. Before the loop starts, insert the following code to initialise the count to zero:
((count = 0))
Then, with each roll of the two dice, use the following to increment the count:
((count = count + 1))
An example of how to do this can be seen below. It's for counting from one to ten but you should get the idea:
((count = 1))
while [[ ${count} -le 10 ]] ; do
echo $count
((count = count + 1))
done
For what it's worth (don't use this if this is a classwork problem, you'd be crazy to think educators don't search the net for plagiarism), here's how I would implement such a beast:
#!/bin/bash
DESIRED="6-6"
((count = 0))
dice="NOT ${DESIRED}"
while [[ "${dice}" != "${DESIRED}" ]] ; do
((count = count + 1))
((die1 = $RANDOM % 6 + 1))
((die2 = $RANDOM % 6 + 1))
dice="${die1}-${die2}"
echo ${dice}
done
echo "It took ${count} rolls to get ${DESIRED}"
I advice to use shuf for this purpose.
#!/bin/bash
declare -i count=1
while [ "6 6" != "$(shuf --input-range='1-6' -r -n 2 | xargs)" ]; do
(( ++count ))
done
echo "It took $count rolls to get double six."
To generating two random numbers between 1 and 6 we use
shuf --input-range='1-6' -r -n 2
shuf [OPTION]... [FILE] write a random permutation of the input lines to standard output. Each output permutation is equally likely. -i lo-hi or --input-range=lo-hi act as if input came from a file containing the range of unsigned decimal integers lo-hi, one per line. -r or --repeat repeat output values, that is, select with replacement. With this option the output is not a permutation of the input; instead, each output line is randomly chosen from all the inputs. -n count or --head-count=count output at most count lines (by default, all input lines are output). If --head-count is not given, shuf repeats indefinitely.
Type man shuf or see coreutils manual for more details.

Bash - Stripping and adding leading zeros to numbers before concatenating into string ordered strings

I need to automate a backup solution which stores files in folders such as YYYYMMDD.nn.
Every day few files would be backed up like this so the resulting folder names could be 20141002.01, 20141002.2 ... 20141002.10. My current script works for YYYYMMDD.n but when n is more than 9 sorting and picking up the last folder doesn't work because 20141002.10 is above 20141002.9 hens switching to YYYYMMDD.nn format and the approach of separating the nn, stripping leading zeros, then incrementing, and adding leading zeros if needed.
I have a function which checks the last folder for today's date and creates the next one.
createNextProcessedFolder() {
local LastFolderName=`ls -1 ${ProcessedListsDir} | grep ${CurrentDate} | tail -n 1`
n=`echo ${LastFolderName} | sed -r 's/^.{9}//'`
n="$((10#$n))"
nextFolderName=${CurrentDate}.$((if[[ $(( ${n}+1 )) < 10 ]];then n="0$((${n}+1))";else n="$(( ${n}+1 ))"; fi))
mkdir ${ProcessedListsDir}/${nextFolderName}
if [[ -d ${ProcessedListsDir}/${nextFolderName} ]]
then
echo "New folder ${nextFolderName} was created"
else
echo "Error: ${nextFolderName} was not created"
fi
Location="${ProcessedListsDir}/${nextFolderName}"
}
So when I try to run this I get an error like:
line 21: if[[ 1 < 10 ]];then n="01";else n="1"; fi: syntax error: invalid arithmetic operator (error token is ";then n="01";else n="1"; fi")
Line 21 is:
nextFolderName=${CurrentDate}.$((if[[ $(( ${n}+1 )) < 10 ]];then n="0$((${n}+1))";else n="$(( ${n}+1 ))"; fi))
I'm sure there will be more errors after this one but I would really appreciate if somebody helped me with this.
You cannot use $((...)) for command substitution as it needs to be $(...)
You need spaces before and after [[ and ]]. You can also use ((...)) in BASH:
Try this:
(( (n+1) < 10 )) && n="0$((n++))" || ((n++))
nextFolderName="${CurrentDate}.${n}"
For completeness, another solution is:
n=$( printf "%02d" $n )
The 02 before the d means prepend with 0s up to 2 digits. Or:
nextFolderName="${CurrentDate}."$( printf "%02d" "$n" )
So my problem was with incrementing a number witch was extracted from a string with a leading zero and then returning the incremented number with a leading zero if smaller than 10. The solution I ended up using can be represented with the below script.
I guess it can't be shorter than that
n=$1
(( ((n++)) < 10 )) && n="0$n"
echo $n
Something I didn't expect is that I don't have to strip leading zeros from n using this, n++ does it while incrementing :-)
Thanks again anubhava for pointing me in the right direction.

Recursive Fibonacci in Bash script

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

Resources