OS Shell Scripting Guide
OS Shell Scripting Guide
This guide covers every concept you need to understand and explain your 6 shell scripts: what shell
scripting is, how each script works line by line, and a complete set of viva questions you should be
ready to answer.
Part 0: What Is Shell Scripting? — The Big
Picture
Before we look at a single line of code, we need to answer a fundamental question: what exactly is a shell,
and why do we write scripts for it? If you skip this section, the syntax in your assignments will feel like
random symbols.
Analogy: The OS kernel is like a powerful engine inside a car. You do not control the engine directly
— you use the steering wheel and pedals. The shell is that interface: it translates your
human-readable commands into precise instructions the engine can act on.
Your assignments use Bash (Bourne Again SHell), which is the most common shell on Linux systems.
Every script begins with the line #!/bin/bash — called a shebang — which tells the OS exactly which
program should interpret the script.
When you run a script with ./[Link], the OS reads the first two characters #! and immediately follows
the path that comes after them to find the right interpreter. So #!/bin/bash means 'run this file using the
Bash shell located at /bin/bash'.
Variables in Bash have no type — everything is treated as text by default. When you want to do arithmetic,
you need special syntax, explained below. The most critical rule is: no spaces around the = sign when
assigning. num = 42 is wrong in Bash; it tries to run a command called 'num' with arguments.
a=10
b=3
echo $((a + b)) # Addition: 13
echo $((a - b)) # Subtraction: 7
echo $((a * b)) # Multiplication: 30
echo $((a / b)) # Division: 3 (integer division — remainder dropped)
echo $((a % b)) # Modulo: 1 (remainder of 10 / 3)
Conditionals — if [ ] and (( ))
• -eq equals -ne not equal -lt less than -le less than or equal -gt greater than -ge greater
than or equal
Inside (( )) you can use ==, !=, <, <=, >, >= just like C/Java.
Now trace for num = 9. 9 is greater than 1. Loop runs for i = 2, 3, 4. 9 % 2 = 1, 9 % 3 = 0 — remainder is
zero! Script immediately prints Not Prime and exits.
Using break would only exit the for loop; the script would then continue and print 'Prime' — which
would be wrong! Using exit terminates the entire script the moment a factor is found, preventing any
further output.
Important: Years divisible by 100 but NOT by 400 are NOT leap years. So 1900 is not a leap year,
but 2000 is.
1 121 1 0*10+1 = 1 12
2 12 2 1*10+2 = 12 1
3 1 1 12*10+1 = 121 0
#!/bin/bash
echo "Enter a number:"
read num
original=$num # Save a copy of the original before we modify num
reverse=0
while [ $num -gt 0 ] # Keep going while there are digits left
do
digit=$((num % 10)) # Extract the last digit
reverse=$((reverse * 10 + digit)) # Append digit to the right of reverse
num=$((num / 10)) # Remove the last digit from num
done
if [ $original -eq $reverse ] # Compare reversed number to original
then
echo "Palindrome"
else
echo "Not Palindrome"
fi
The while loop destroys $num by dividing it repeatedly until it becomes 0. If we didn't save the original
value first, we would have nothing to compare the reversed number against at the end. This is why
line 4 saves a copy before the loop begins.
Note on the definition: A fully general Armstrong number uses the number of digits as the power (so
a 4-digit number would use 4th power). Your assignment uses cube (power of 3) specifically, which
works correctly for 3-digit numbers.
Start 153 — — 0
1 153 3 27 27
2 15 5 125 152
3 1 1 1 153
The algorithm needs to track two variables — let's call them a (current) and b (next). At each step, we print
a, calculate the new next term, then shift both variables forward.
#!/bin/bash
echo "Enter number of terms:"
read n
a=0 # First Fibonacci number
b=1 # Second Fibonacci number
echo "Fibonacci Series:"
for (( i=1; i<=n; i++ ))
do
echo -n "$a " # Print current term; -n prevents newline (stays on same line)
temp=$((a + b)) # Calculate the NEXT term before overwriting a
a=$b # Shift: a moves to where b was
b=$temp # Shift: b moves to the new next term
done
echo # Print a newline at the end (because echo -n suppressed them all)
1 0 0+1=1 1 1
2 1 1+1=2 1 2
3 1 1+2=3 2 3
4 2 2+3=5 3 5
5 3 3+5=8 5 8
A: A shell script is a text file containing a sequence of shell commands that can be executed as a
single program. It allows automation of repetitive tasks in a Linux/Unix environment. Shell scripts are
interpreted line by line by the shell program, not compiled.
A: It is called a shebang line. The #! characters tell the OS that this is a script file, and /bin/bash is the
path to the Bash interpreter that should execute it. Without this line, the OS does not know which
interpreter to use.
Q: What is the difference between single quotes and double quotes in Bash?
A: Double quotes allow variable expansion — $variable inside double quotes is replaced by its value.
Single quotes treat everything literally — $variable inside single quotes is printed as the literal text
'$variable', not its value.
A: The read command pauses the script and waits for the user to type input. Whatever the user types
is stored in the named variable. For example, 'read num' stores the typed value in the variable $num.
A: The modulo operator % gives the remainder of integer division. For example, 7 % 3 = 1 because 7
divided by 3 gives 2 with a remainder of 1. It is used in nearly every mathematical script to extract
digits, check divisibility, and reverse numbers.
A: Square brackets [ ] use a special comparison syntax with flags like -eq, -lt, -gt for numbers and =
for strings. Double parentheses (( )) use standard arithmetic operators like ==, <, >, &&, || and are
generally more natural for mathematical conditions.
Q: What does echo -n do?
A: Normally, echo prints its output followed by a newline character, moving the cursor to the next line.
The -n flag suppresses this newline, so the next echo output appears on the same line. The Fibonacci
script uses this to print all terms on one line separated by spaces.
A: Bash, like most Unix tools, follows Unix conventions where identifiers are case-sensitive. The
interpreter only recognises keywords like 'if', 'then', 'while', 'do', 'done', 'fi' in lowercase. Writing 'If' or
'While' causes Bash to look for a command with that exact name, which does not exist, resulting in a
'command not found' error.
Q: Why does the prime number script check up to num/2 and not num?
A: If a number N has a divisor d greater than N/2, then N/d would be less than 2, which is impossible
for integer divisors. So no factor of N can be larger than N/2. This optimisation halves the number of
iterations needed.
A: 2 is greater than 1, so it passes the first check. The for loop runs for i from 2 to 2/2 = 1. Since the
starting value 2 is greater than the ending value 1, the loop body never executes. The script reaches
the final echo and prints 'Prime'. This is correct — 2 is prime.
Q: Explain the leap year condition: (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0).
A: This is a direct implementation of the Gregorian calendar rule. The first part handles century years
divisible by 400 (always leap years, like 2000). The second part handles regular years: divisible by 4
but not by 100 (like 2024, 1996). The year 1900 fails because it is divisible by 100 but not 400.
A: The while loop modifies the variable by dividing it by 10 repeatedly until it reaches 0. Once the loop
finishes, the original variable holds 0. We need the original value to compare against the reversed
number (palindrome) or the digit sum (Armstrong). Saving a copy before the loop preserves this
value.
A: The temp variable stores the sum a+b before we overwrite a. If we wrote 'a=$b' first and then
'b=$((a+b))', the value of 'a' would already be the new value, giving a wrong result. temp acts as a
safe holding place during the simultaneous shift of both variables.
Q: Why does the binary conversion script use binary="$rem$binary" instead of
binary="$binary$rem"?
A: The division-by-2 algorithm produces the least significant bit (rightmost) first. Appending with
$binary$rem would build the number backwards. Prepending with $rem$binary places each new bit
to the left of all previously found bits, which naturally produces the correct binary representation from
most significant to least significant.
Q: What is the binary representation of 10, and can you trace the script?
A: 10 in binary is 1010. Trace: 10%2=0, binary='0', num=5. 5%2=1, binary='10', num=2. 2%2=0,
binary='010', num=1. 1%2=1, binary='1010', num=0. Loop ends. Output: 1010.
A: The 3-digit Armstrong numbers are 153, 370, 371, and 407. Verification: 3³+7³+1³ = 27+343+1 =
371 ✓. These are the only four 3-digit numbers where the sum of cubed digits equals the number
itself.
A: The while loop condition is [ $num -gt 0 ], which is false for 0. The loop never executes, reverse
remains 0, and original is 0. Since 0 equals 0, the script prints 'Palindrome'. This is mathematically
reasonable — 0 reads the same forwards and backwards.
A: The algorithm runs in O(N/2) time, which simplifies to O(N). A more efficient version would check
only up to the square root of N, giving O(√N) complexity, but your assignment uses the N/2 approach
which is sufficient for the scope of this lab.
Q: Name five Fibonacci numbers and the term number that generates them.
A: Term 1: 0. Term 2: 1. Term 3: 1. Term 4: 2. Term 5: 3. Term 6: 5. Term 7: 8. Term 8: 13. Each term
is the sum of the two immediately before it.
A: The while loop condition [ $num -gt 0 ] would be false immediately for 0, so the loop never
executes. The variable 'binary' remains an empty string. The script would print 'Binary number: ' with
nothing after it. In a production script, you would add a special case to handle 0 and print '0'.
A: Bash would treat num+1 as a literal string and assign the text 'num+1' to the variable num. There
would be no arithmetic. This is one of the most common beginner mistakes in Bash. All arithmetic in
Bash must be performed inside $(( )).
A: The command would fail with a 'command not found' error because Bash looks for a command
literally called 'if[$num'. The square brackets in Bash are actually a command called 'test', and they
require spaces on all sides: [ $num -eq 0 ].
Q: What is the bug in the original Q3 and Q5 code from the assignment?
A: The original code uses capitalised keywords: Echo, Read, While, Do, Done, If, Then, Else, Fi, For.
Bash is case-sensitive and only recognises these as lowercase keywords. Capitalised versions are
treated as command names, causing 'command not found' errors at runtime.
A: First, make the script executable: chmod +x [Link]. Then run it: ./[Link]. Alternatively, you can
invoke the interpreter directly: bash [Link] — this does not require the file to have execute
permissions.
Part 8: Quick Revision Cheat Sheet
Prime Divide by i from 2 to N/2; if % (modulo) Use exit not break; handle <=1
remainder==0, not prime separately
Leap Year Divisible by 400 OR (by 4 AND not || and && Century exception: 1900 is
100) NOT a leap year
Palindrome Extract digits via %10, build % 10 and / 10 Save original before loop; use
reverse, compare to original lowercase keywords
Armstrong Extract digits via %10, add digit³ to digit*digit*digit Only correct for 3-digit numbers
sum, compare to original as written
Fibonacci Print a, temp=a+b, a=b, b=temp, Swap via temp temp prevents overwrite bug;
repeat n times echo -n for inline print
For every script, be ready to: (1) explain what the problem is mathematically, (2) trace through a
specific example by hand, and (3) explain why each line of code is written the way it is. Examiners
love asking 'what would happen if...' questions — the answers are all covered in Category D of the
viva section above.