Shell script read user input - bash

I got stuck on this shell script code where it requires user to enter the group number and find the largest and the average out of that group number.
My code ATM only works with passing the group number as a command-line argument. How to prompt for the group number as user input?
read n
if [ $n -ge 1 ]; then
sum=0
count=$n
max=-1000
if [ $max -lt $1 ]; then
max=$1
fi
while [ $n -ge 1 ]; do
case $1 in
[0-9] | [1-9][0-9])
sum=`expr $sum +$1`;;
-[1-9] | -[1-9][0-9])
sum=`expr $sum + $1`;;
done
if [ $count -gt 0 ]; then
avg=`expr $sum / $count`
echo The largest number is $max
echo The average number is $avg

From your comments it seems you would like to read values from stdin rather than from the command line. To do that in Bash you use the read builtin:
read -ep "Enter group number: " group
printf "Entered %d\n" $group
For interactive prompting it is usually put in the test part of a while loop where you can break if the input is invalid:
shopt -s extglob
while read -ep "Enter group number: " group; do
case $group in
?(-)+([0-9])) # valid input
# compute average here
*) break ;; # not valid input
esac
done
See help read for more information.

You're working way too hard, and your script is way too verbose. Try:
#!/bin/sh
test $# -gt 0 || { echo Please enter at least one argument >&2; exit 1; }
max=0
for x; do
test "$max" -lt $x && max=$x
test $? -gt 1 && exit 1
: $(( count += 1 ))
: $(( sum += x ))
done
echo max = $max
printf "avg = "
expr $sum / $count
Since you used expr in your script, I'm doing the same, but be aware that all of the arithmetic will be done in the integers (so the reported average will be the greatest integer less than the actual average, and non-integer input will be considered an error). This solution relies on test returning a value greater than 1 when it encounters an error (eg, non integer input), which is the behavior specified by the open group.
Also note that this puts the error message on stderr (where error messages belong) and returns a non-zero value to indicate that the script failed.

Related

How to fix count that doesn't work in while loop

I have been trying to resolve an issue where my loop's count should decrease, however nothing is working. I need to create a while loop that will read over a given amount of times. For instance, if I enter in "files.txt -a 3" in the terminal, I need my loop to repeat "Enter in a string: " 3 times. With my code below, I am only able to get it to loop once. I am not to sure where to put the counter and I can say that I have put it everywhere. Inside the if statement, in inside of the for loop, and inside the while loop but none seem to work. The number that the user will put is held in the $count variable.
#!/bin/bash
if ["$1" = "-a" ]
then
read in user String and save into file
fi
while [ "$count" > 0 ]
do
for i in $count
do
if [ "-a" ]
then
read -p "Enter in a string: " userSTR
echo userSTR >> files.txt
count=$(($count - 1))
fi
done
done
For conditional expression you need to use [[ expression ]], e.g. this will loop four times:
count=4
while [[ $count > 0 ]] ; do
echo "$count"
count=$(( $count - 1 ))
done
To fetch the count from the command-line argument, you could replace the assignment count=4 above with the following, parsing the command-line arguments:
if [ $# -lt 2 ] ; then
echo "Usage: $0 -a [count]"
exit 1
fi
if [ "$1" = "-a" ] ; then
shift
count=$1
fi

Shell Script command not found error

Below my script for up to n prime numbers. When I run it, it always shows an error that command not found in line 12 and 18 both. What am I doing wrong?
clear
echo"enter the number upto which you want prime numbers : "
read n
for((i=1;i<=n;i++))
do
flag=0
for((j=2;j<i;j++))
do
if [expr $i % $j-eq 0]
then
flag=1
fi
done
if [$flag-eq 0]
then
echo $i
fi
done
As pointed out in comments, you must use spaces around [ and ], as well as the comparison operators. Even more safe when using [ and ] is quoting your variables to avoid word splitting (not actually required in this specific case, though).
Additionally, you want to compare the output of expr to 0, so you have to use command substitution:
if [ $(expr "$i" % "$j") -eq 0 ]
and
if [ "$flag" -eq 0 ]
Since you're using Bash, you can use the (( )) compound command:
if (( i % j == 0 ))
and
if (( flag == 0 ))
No expr needed, no command substitution, no quoting required, no $ required, and the comparison operators have their "normal", expected meaning.
There are a number of syntax errors other than the brackets of if statement. Kindly go through the piece of code below. I have checked it running on my system.
#!/bin/sh
echo "enter the number upto which you want prime numbers : "
read n
for((i=1;i<=n;i++))
do
flag=0
for((j=2;j<i;j++))
do
if [ `expr $i % $j` -eq 0 ]
then flag=1
fi
done
if [ $flag -eq 0 ]
then echo $i
fi
done

Shell script to output the largest

How to write a script which will receive a list of parameters and output the number that is the largest. If no parameters are supplied, output an error message.
I wrote the following code to check if no parameters are supplied, output an error message.
#!/bin/bash
if [ "$#" -eq "0" ]
then
echo "No arugments supplied"
else
echo "$# Parameter"
But I dont know how to continue...
Keep a current max. Loop over the input, updating max if necessary. At the end you'll have the global maximum.
Untested:
#!/usr/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 NUMBERS" >&2
exit 1;
fi
max="$1"
shift
while [ $# -gt 0 ]; do
if [ "$1" -gt "$max" ]; then
max="$1"
fi
shift
done
echo "$max"
Use sort -n (numeric) and -r (reverse) and then just pick the first line of the output -- like
#!/bin/bash
if [ "$#" -eq "0" ]
then
echo "No arugments supplied"
else
echo "$# Parameter"
for i in $*; do echo ${i}; done | sort -nr | head -1
fi
Now the only problem you are facing is when the the input (the arguments) are not numbers -- but you didn't say anything about what should happen then.
Here's a pseudocode you could implement:
save the first param in a variable called max
loop over the params
if the param is greater than max, update max
print max
Here's an example loop that prints all parameters:
for num; do
echo $num
done
And here's an example of comparing values:
if (( num > max )); then
echo $num is greater than $max
fi
This should be more than enough help to complete your homework.
Well since others have already gave you the actual solution, here's mine too:
#!/bin/bash
if (( $# == 0 )); then
echo "No arugments supplied"
exit 1
fi
max=$1
for num; do
if (( num > max )); then
max=$num
fi
done
echo $max

Counting down in a loop to zero by the number being given

I am trying to write a while loop to determine the number is being given to count down to 0. Also, if there's no argument given, must display "no parameters given.
Now I have it counting down but the last number is not being 0 and as it is counting down it starts with the number 1. I mush use a while loop.
My NEW SCRIPT.
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo $#
fi
COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=COUNT-1
done
echo Finished!
This is what outputs for me.
sh countdown.sh 5
1
5
4
3
2
1
Finished!
I need it to reach to 0
#Slizzered has already spotted your problem in a comment:
You need operator -ge (greater than or equal) rather than -gt (greater than) in order to count down to 0.
As for why 1 is printed first: that's simply due to the echo $# statement before the while loop.
If you're using bash, you could also consider simplifying your code with this idiomatic reformulation:
#!/usr/bin/env bash
# Count is passed as the 1st argument.
# Abort with error message, if not given.
count=${1?No parameters given}
# Count down to 0 using a C-style arithmetic expression inside `((...))`.
# Note: Increment the count first so as to simplify the `while` loop.
(( ++count ))
while (( --count >= 0 )); do
echo $count
done
echo 'Finished!'
${1?No parameters given} is an instance of shell parameter expansion
bash shell arithmetic is documented here.
You should also validate the variable before using it in an arithmetic context. Otherwise, a user can construct an argument that will cause the script to run in an infinite loop or hit the recursion limit and segfault.
Also, don't use uppercase variable names since you risk overriding special shell variables and environment variables. And don't use [ in bash; prefer the superior [[ and (( constructs.
#!/usr/bin/env bash
shopt -s extglob # enables extended globs
if (( $# != 1 )); then
printf >&2 'Missing argument\n'
exit 1
elif [[ $1 != +([0-9]) ]]; then
printf >&2 'Not an acceptable number\n'
exit 2
fi
for (( i = $1; i >= 0; i-- )); do
printf '%d\n' "$i"
done
# or if you insist on using while
#i=$1
#while (( i >= 0 )); do
# printf '%d\n' "$((i--))"
#done
Your code is far from being able to run. So, I don't know where to start to explain. Let's take this small script:
#!/bin/sh
die() {
echo $1 >&2
exit 1;
}
test -z "$1" && die "no parameters given"
for i in $(seq $1 -1 0); do
echo "$i"
done
The main part is the routine seq which does what you need: counting from start value to end value (with increment in between). The start value is $1, the parameter to our script, the increment is -1.
The test line tests whether there is a parameter on the command line - if not, the script ends via the subroutine die.
Hth.
There are a number of ways to do this, but the general approach is to loop from the number given to an ending number decrementing the loop count with each iteration. A C-style for loop works as well as anything. You will adjust the sleep value to get the timing you like. You should also validate the required number and type of input your script takes. One such approach would be:
#!/bin/bash
[ -n "$1" ] || {
printf " error: insufficient input. usage: %s number (for countdown)\n" "${0//*\//}"
exit 1
}
[ "$1" -eq "$1" >/dev/null 2>&1 ] || {
printf " error: invalid input. number '%s' is not an integer\n" "$1"
exit 1
}
declare -i cnt=$(($1))
printf "\nLaunch will occur in:\n\n"
for ((i = cnt; i > 0; i--)); do
printf " %2s\n" "$i"
sleep .5
done
printf "\nFinished -- blastoff!\n\n"
exit 0
Output
$ bash ./scr/tmp/stack/countdown.sh 10
Launch will occur in:
10
9
8
7
6
5
4
3
2
1
Finished -- blastoff!
Your Approach
Your approach is fine, but you need to use the value of COUNT $COUNT in your expression. You also should declare -i COUNT=$1 to tell the shell to treat it as an integer:
#!/bin/bash
if [ $# -eq "0" ] ;then
echo "No paramters given"
else
echo -e "\nNumber of arguments: $#\n\n"
fi
declare -i COUNT=$1
while [ $COUNT -gt 0 ] ;do
echo $COUNT
let COUNT=$COUNT-1
done
echo -e "\nFinished!\n"

Is there an easy way to determine if user input is an integer in bash?

I am a new student to bash scripting, and I am stumped on an assignment question.
I was wondering if there is an easy way to determine whether a users' input is an integer or not. More specifically, if a user is prompted to input an integer, is there a quick check to validate?
One way is to check whether it contains non-number characters. You replace all digit characters with nothing and check for length -- if there's length there's non-digit characters.
if [[ -n ${input//[0-9]/} ]]; then
echo "Contains letters!"
fi
Another approach is to check whether the variable, evaluated in arithmetic context, is equal to itself. This is bash-specific
if [[ $((foo)) != $foo ]]; then
echo "Not just a number!"
fi
This is kind of a kludge, it's using -eq for something other then what it was intended, but it checks for an integer, if it doesn't find an int it returns both an error which you can toss to /dev/null and a value of false.
read input
if [[ $input ]] && [ $input -eq $input 2>/dev/null ]
then
echo "$input is an integer"
else
echo "$input is not an integer or not defined"
fi
You can test by using Regular expression
if ! [[ "$yournumber" =~ ^[0-9]+$ ]] ;
then exec >&2; echo "error: Not a number"; exit 1
fi
I found this post http://www.unix.com/shell-programming-scripting/21668-how-check-whether-string-number-not.html that talks about this.
If your input does not need to check if there is a +/- on the number, then you can do:
expr $num + 1 2> /dev/null
if [ $? = 0 ]
then
echo "Val was numeric"
else
echo "Val was non-numeric"
fi
Here is another way of doing it. It's probably a bit more elaborate than needed in most cases, but would handle decimals also. I had written the below code to get rounded number. It also checks for numeric input in the process.
#--- getRound -- Gives number rounded to nearest integer -----------------------
# usage: getRound <inputNumber>
#
# echos the rounded number
# Best to use it like:
# roundedNumber=`getRound $Number`
# check the return value ($?) and then process further
#
# Return Value:
# 2 - if <inputNumber> is not passed, or if more arguments are passed
# 3 - if <inputNumber> is not a positive number
# 0 - if <inputNumber> is successfully rounded
#
# Limitation: Cannot be used for negative numbers
#-------------------------------------------------------------------------------
getRound (){
if [ $# -ne 1 ]
then
exit 2
fi
#--- Check if input is a number
Input=$1
AB=`echo A${Input}B | tr -d [:digit:] | tr -d '.'`
if [ "${AB}" != "AB" ] #--- Allow only '.' and digit
then
exit 3
fi
DOTorNone=`echo ${Input} | tr -d [:digit:]` #--- Allow only one '.'
if [ "${DOTorNone}" != "" ] && [ "${DOTorNone}" != "." ]
then
exit 3
fi
echo $Input | awk '{print int($1+0.5)}' #--- Round to nearest integer
}
MyNumber=`getRound $1`
if [ $? -ne 0 ]
then
echo "Empty or invalid input passed"
else
echo "Rounded input: $MyNumber"
fi
This one works for me, handling empty input case.
if [ $input -eq $input 2>/dev/null -o $input -eq 0 2>/dev/null ]
then
echo Integer
else
echo Not an integer
fi

Resources