1

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

mklement0
  • 382,024
  • 64
  • 607
  • 775
Mark
  • 27
  • 1
  • 1
  • 4
  • Use http://www.shellcheck.net/ – Cyrus Apr 18 '15 at 17:57
  • 1
    to reach 0, replace `[ $COUNT -gt 0 ]` with `[ $COUNT -ge 0 ]` (**gt** is **g**reater **t**han, **ge** is **g**reater or **e**qual) So you will do 1 more iteration. – Slizzered Apr 18 '15 at 18:54
  • 1
    @Mark: That `1` as the first output line is printed by the `echo $#` command before the loop. – mklement0 Apr 18 '15 at 19:12
  • 1
    @mklement0 is right. `$#` will print the number of arguments. Maybe you wanted to use `$@`, which will print the arguments themselves. – Slizzered Apr 18 '15 at 19:23

4 Answers4

2

@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!'
mklement0
  • 382,024
  • 64
  • 607
  • 775
1

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
geirha
  • 5,801
  • 1
  • 30
  • 35
  • Good advice and nice solution. Please note that using extended globs such as `+([0-9])` inside `[[ ... ]]` without first running `shopt -s extglob` requires bash 4.1 or higher. A safer choice is `[[ ! $1 =~ ^[0-9]+$ ]]`. – mklement0 Apr 18 '15 at 19:32
  • 1
    @mklement0, yes that's why I put `shopt -s extglob` at the top. It can also be done with a regular glob: `[[ $1 = *[!0-9]* ]]` (or `case $1 in *[!0-9]*) ...`). – geirha Apr 18 '15 at 19:51
  • Oops! Sorry I missed the `shopt -s extglob`. Good point re regular globs - might be simpler in this case. – mklement0 Apr 18 '15 at 19:59
  • `then` in a for loop just doesn't work. Nice tested answer. – Deanie Feb 15 '20 at 18:17
  • @Deanie well that's embarrassing. Fixed it now though. Thank you. – geirha Feb 15 '20 at 21:14
0

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.

leu
  • 2,051
  • 2
  • 12
  • 25
  • A solid solution, if POSIX compliance is needed. The last `seq` parameter should be `0`, though; perhaps double-quote _all_ `$1` instances, to be safe. – mklement0 Apr 18 '15 at 19:16
  • 1
    @mklement0, `seq` is not a standard command, so I wouldn't exactly call it POSIX compliant; especially since POSIX sh can already do the counting just fine without external tools. – geirha Apr 18 '15 at 19:54
  • @geirha: Thanks for pointing out that `seq` is not a POSIX utility - I was only paying attention to the POSIX-compliant _shell language_ constructs. – mklement0 Apr 18 '15 at 20:01
-1

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"
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85