|

[Bash Challenge] Can You Solve This Bash Scripting Exercise?

If you follow It’s FOSS on Facebook, you might be aware of the weekly Bash Challenge. It is a joint effort by Yes I Know It and It’s FOSS to give you a Bash script exercise to test your Linux skills.

We are bringing this Bash Challenge from Facebook to a wider audience on the regular web. This is the 5th installment of this series. The first 4 challenges can be found on our Facebook pages. You can also buy these challenges in book form:

[irp posts=”17248″ name=”Bash It Out! Bash Script Puzzle Book by It’s FOSS is Available Now!”]

Bash Challenge 5

We will show you a terminal screenshot, and ask you to explain why the result is not the one we were expecting. Of course, the most amusing, and most creative, part of the challenge will be to find how to fix the command(s) displayed on the screen to obtain the correct result.

Ready to play? So here is this week’s challenge:

My Bash don’t know how to count [Difficulty level 1]

Bash Challenge 5: Bash Scripting exercise

This week, I have some data file containing integer numbers, one on each line:

cat sample.data
102
071
210
153

And I want to compute the sum of all those numbers:

declare -i SUM=0
while read X ; do
    SUM+=$X
done < sample.data
echo "Sum is: $SUM"

Unfortunately, the result I obtain is wrong (the expected result was 536):

Sum is: 522

Challenge

Your challenge is to find :

  • Why that result was wrong?
  • How to fix my commands to obtain the correct result?

★ Bonus unicorn point if you can find a solution using only Bash internal commands and/or shell substitutions.

We’re looking forward to read your solutions in the comment section below! Don’t forget to be creative.

Few details

To create this challenge, I used:

  • GNU Bash, version 4.4.5 (x86_64-pc-linux-gnu)
  • Debian 4.8.7-1 (amd64)
  • All commands are those shipped with a standard Debian distribution
  • No command was aliased

Solution

How to reproduce

Here is the raw code we used to produce this challenge. If you run that in a terminal, you will be able to reproduce exactly the same result as displayed in the challenge illustration (assuming you are using the same software version as me):

rm -rf ItsFOSS
mkdir -p ItsFOSS
cd ItsFOSS
cat > sample.data << 'EOT'
102
071
210
153
EOT
clear
cat sample.data
declare -i SUM=0
while read X ; do
    SUM+=$X
done < sample.data
echo "Sum is: $SUM"

What was the problem ?

The problem was caused by the 071 value. As you noticed, this number is starting by a 0 — probably to ensure here all data are formatted on three digits. Nothing complicated here, except that … following an unfortunate convention inherited from the C programming language, prefixing an integer by 0 is a way to specify that number is expressed in octal, and not in decimal.

Octal numbers are expressed with digits from 0 to 7. Here is a simple conversion table:

Octal Decimal
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
10 8
11 9
12 10
13 11
14 12
.. ..
71 57

This last value is what caused the error when evaluating the sum. The Bash read 071 and interpreted it as an octal number representing the 57 decimal value. You can check that easily:

echo $((071))
57

How to fix that ?

I can see two main strategies to fix that issue. Either removing the leading zeros. Or finding a way to make the shell understand all my numbers are decimal values.

Removing leading zeros

Here is a simple solution using the sed external command to remove the leading zeros :

declare -i SUM=0
while read X ; do
    SUM+=$X
done < <(sed -E s/^0+// sample.data)
echo "Sum is: $SUM"

(bonus question: why didn’t I used a pipe instead of a process substitution ?)

Specifying explicitly the base

The previous solution is (mostly) straightforward — but the Bash allows us to make things better. Instead of trying to fix the data, we will simply specify explicitly our numbers are expressed in base 10 (decimal), instead of base 8 (octal). You can do that by using the base#value syntax.

Compare those three examples:

echo $((071))        # The leading `0` specify the number as octal
57
echo $((8#071))      # We *explicitly* specify base 8 (octal)
57
echo $((10#071))     # We *explicitly* specify base 10 (decimal)
71

To fix my initial command and obtain the correct result, I only have to explicitly specify the base 10 for all my data :

declare -i SUM=0
while read X ; do
    SUM+=$((10#$X))
done < sample.data
echo "Sum is: $SUM"

And here is the correct result. We hope you enjoyed that challenge. Stay tuned for more fun!

Author Bio: I’m Sylvain Leroux, a software engineer by passion, a teacher by vocation. I have 15 years of experience in teaching Computer Science & Information Technologies at all level. I’m a strong advocate of Linux & OpenSource technologies. I founded Yes I Know IT to share that experience with a wider audience through online courses & free videos. Don’t hesitate to reach me on Twitter.

Similar Posts

  • Write a script in the bash shell which verifies whether a user is an authorized person to use the system or not. If the person is authorized, the script indicates whether that person is currently logged in or not.

    please can anyone help me with this exercise ?