-1
#!/bin/bash
# Show help function
# Call with: -h 
function help(){ cat << EOF
Arguments:
Calculate the sum of several numbers. Example: $sum 19 115 21 
                                                    10  7   3
As soon as a letter is given in the argument, the program issues an error message. 
EOF
}

#Main program
if [ "$1" == "-h" ] ; then
        help
        exit
    fi
check='^[0-9]+$'
    if ! [[ $var =~ $check ]] ; then
        echo "error: Caution! It is not a number!"
            continue
    fi

x=${1//[^0-9]/}
sum=0

for ((i=0; i<${#x}; i++));

while [[ num -gt 0 ]];do
var=$(($num % 10))
num=$(($num / 10))
sum=$(($sum + $var))
done

echo $sum

The program must receive one or more numbers as arguments and calculate the sum of digits for each specified number. I don't get anything. How can the program also receive and calculate several numbers as arguments? If loops and while I have used. The explanation must be up to 80 characters.

3
  • 1
    @Jana, have a look at the editing help, the first point about code block formatting, and please put some thought into how you indent your code; you'll see it makes it far more readable, and makes some issues (like the for loop missing do and done) rather more evident. Also, shellcheck.net is a good tool for debugging shell scripts, it can automatically point out a number of issues.
    – ilkkachu
    Nov 12, 2021 at 16:30
  • @ilkkachu Thank you so much! I will check it in Shellcheck.
    – Jana
    Nov 12, 2021 at 17:09
  • 1
    You have commas in your title data example: 19 10,13 4, 12 3, but none in your $sum example: 19 115 21 . Which is it? And if commas are important, what do they signify? Nov 20, 2021 at 21:12

5 Answers 5

1

Here's a way to iterate over the digits of a number and sum them:

sum_digits() {
    local num=$1 len=${#1} sum=0 digit i
    for ((i=0; i<len; i++)); do
        digit=${num:i:1}
        sum=$((sum + digit))
    done
    echo "$sum"
}

then

$ sum_digits 123
6
$ sum_digits 987654321
45

${var:offset:length} is the shell's parameter expansion to extract a substring out of the variable's value.

2
  • Thank you! But the digit sum must receive and calculate several numbers. For example: ./script 12 23 24 231 Answer: 12 = 3 23 = 5 24 = 6 231 = 6 But your solution just gets a number and calculates it.
    – Jana
    Nov 12, 2021 at 16:28
  • you'll iterate over the numbers, and in the loop you'll call this function. Break the problem into smaller steps Nov 12, 2021 at 16:33
0

I think you are making it much complicated than it should be. See if this is useful to you

#!/bin/bash

die()
{
  echo >&2 "$@"
  exit 1
}

usage()
{
  echo >&2 "Usage: ....."
  die
}

calc() 
{ 
  if (( $# < 1 )); then usage; fi
  local args="$@"; 
  re='^[0-9]+$'
  total=0
  for n in ${args[@]}; do
   [[ $n =~ $re ]] || die "A non digit was provided"
   ((total += n))
  done
  echo "$total"
}
calc "$@"

You'll execute the script as

./script 22 20

And there is your answer.

3
  • Thank you! But My Example is: The sum of the digits or the checksum of a number is the sum of the individual digits of a number. E.g .: 123 = 1 + 2 + 3 = 6 or 84 = 8 + 4 = 12. Not 22 + 20 = 42
    – Jana
    Nov 12, 2021 at 13:06
  • Ah nou that makes sense. So two consecutive numbers should add up?! Nov 12, 2021 at 13:17
  • Yes, exactly. 22 is 4 , 20 is 2 , 11 is 2
    – Jana
    Nov 12, 2021 at 13:40
0

Note: This is not a solution to your problem, just a pointer how to tidy up the code that you already have and why it won't work, as it stands.


Firstly, you need to consistently indent your code, else it will look a mess, and you can easily get lost.

Secondly, to get your code running without syntax errors you need to fix a few things:

  • a missing $ on the num in the while conditional
  • missing do and done for the for loop.

Fixing these errors, indenting and removing some of the excessive blank lines, your code becomes:

#!/bin/bash
# Show help function
# Call with: -h
function help(){
cat << EOF
Arguments:
Calculate the sum of several numbers. Example: $sum 19 115 21
                                                    10  7   3
As soon as a letter is given in the argument, the program issues an error message.
EOF
}

# Main program

if [ "$1" == "-h" ] ; then
  help
  exit
fi

check='^[0-9]+$'
if ! [[ $var =~ $check ]] ; then
   echo "error: Caution! It is not a number!"
   continue
fi

x=${1//[^0-9]/}
sum=0
for ((i=0; i<${#x}; i++)) ; do
  while [[ $num -gt 0 ]] ; do
    var=$(($num % 10))
    num=$(($num / 10))
    sum=$(($sum + $var))
  done
done
echo $sum

Now it should run, but it still won't do what you want it to.

To begin with, you don't assign num (nor var) at any point in the code. You probably mean to assign them to each of the arguments in turn:

  • By not assigning var, you will always get the "This is not a number" error
  • By not assigning num, your sum will always be zero
0

I would suggest a separation of the number validity checking and digit sum calculations from the iteration over the command-line arguments. That might make things easier to read.

The following example reports all errors encountered (and not just the first one), “because we can”.

It doesn’t support negative numbers, but Bash does, so a trivial modification to the glob (such as @(0|-) instead of 0) could add that support if needed. (Then I’m not sure what the digit sum should be; maybe positive, maybe negative.)

#!/bin/bash
shopt -s extglob
set -uo pipefail

sum_digits_in_number() {
  if [[ "$1" != @(0|[1-9]*([0-9])) ]]; then
    echo -n " [['${1}' is not a number]]" 1>&2
    return 1
  fi
  local -i n="$1" sum=0
  while ((n)); do
    ((sum += n % 10)) || :
    ((n /= 10)) || :
  done
  echo -n " $((sum))"
}

sum_digits() {
  local arg
  local -i errors=0
  for arg in "$@"; do
    sum_digits_in_number "$arg" || ((++errors))
  done
  if ((errors)); then
    echo -e "\\n$((errors)) error(s)." 1>&2
    return 1
  fi
  echo
}

Let’s test it!

# Valid calls:
sum_digits 19 115 21
sum_digits 0 
sum_digits 1 0
sum_digits {100..0}
sum_digits {1..100}

# Wrong calls:
sum_digits 00         # 1 error
sum_digits {0..10} z  # 1 error
sum_digits z {0..10}  # 1 error
sum_digits 1z 3 z1    # 2 errors
sum_digits a b c d    # 4 errors

Alternatively, you could also sum the digits by slicing the integer $n as if it was a string, like so:

sum_digits_in_number() {
  if [[ "$1" != @(0|[1-9]*([0-9])) ]]; then
    echo -n " [['${1}' is not a number]]" 1>&2
    return 1
  fi
  local -i n="$1" sum=0 i
  for ((i = 0; i < ${#n}; ++i)); do
    ((sum += ${n:i:1}))
  done
  echo -n " $((sum))"
}

Last but not least, the || : suffix is there only to make the script work (if desired) (also) in the set -e mode. Otherwise, if the last digit is zero, then ((sum += n % 10)) “fails”. One could instead do sum+='n % 10', which would work nicely (using integer evaluation as expected) and never fail. Also, notice that the second version of sum_digits_in_number doesn’t have a || :, simply because it iterates from the first digit rather than the last and, by prohibiting 0 as the first digit (to avoid confusion with octal numbers, e.g. echo $((010)) says 8), we can be sure that the += will always “succeed”.

0

Using Raku (formerly known as Perl_6)

raku -e '.put for @*ARGS.map( *.comb).map( *.sum);' 19 115 21 22 20

Output:

10
7
3
4
2

Above is a solution coded in Raku, a member of the Perl-family of languages. Briefly, command line values are accepted into the @*ARGS array, each value is split into individual characters (comb-ed), a individual characters are summed.

Note, if you're interested in playing with numbers, Raku provides some very clever Rat-ional number functionalities , such as the nude ("NUmerator DEnominator") function below.

raku -e ' say( .nude, " == ", .Num ) for @*ARGS.map( *.Rat);' 1 2/2 1/2 3/3 2/3 1/3 4/4 3/4 2/4 1/4

Output:

(1 1) == 1
(1 1) == 1
(1 2) == 0.5
(1 1) == 1
(2 3) == 0.6666666666666666
(1 3) == 0.3333333333333333
(1 1) == 1
(3 4) == 0.75
(1 2) == 0.5
(1 4) == 0.25

https://docs.raku.org/language/variables#index-entry-@*ARGS
https://docs.raku.org/type/Rational
https://raku.org/

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .