Author: admin

  • Master Loop in Shell Script : for, while, and until (Beginner’s Guide 2026)

    Loop in Shell Script : Repeating tasks is a common need in programming, and Bash scripting makes it super simple with loops. Whether you’re a total beginner or just brushing up, this guide will walk you through for, while, and until loops in Bash in a clear and practical way.

    Loop in Shell Script :

    Why Loops Matter in Bash

    Loops let you automate repetitive tasks, saving time and reducing errors. Imagine printing numbers, processing files, or running commands multiple times — loops make it all easy.

    In Bash, there are three main types of loops:

    • for loop – great for iterating over a list or range
    • while loop – runs as long as a condition is true
    • until loop – runs until a condition becomes true

    Let’s explore each with easy examples!

    The for Loop

    The for loop is perfect when you know how many times you want to repeat a task.

    Syntax:

    for variable in list
    do
      # commands
    done
    

    Example 1: Print numbers 1 to 5

    for i in 1 2 3 4 5
    do
      echo "Number: $i"
    done
    

    Example 2: Loop through a list of names

    for name in Alice Bob Charlie
    do
      echo "Hello, $name!"
    done
    

    The while Loop

    The while loop continues as long as a condition is true.

    Syntax:

    while [ condition ]
    do
      # commands
    done
    

    Example: Count from 1 to 5

    count=1
    while [ $count -le 5 ]
    do
      echo "Count: $count"
      ((count++))
    done
    

    Tip: Always make sure your condition will eventually become false to avoid infinite loops!

    The until Loop

    The until loop is like the opposite of while. It runs until the condition is true.

    Syntax:

    until [ condition ]
    do
      # commands
    done
    

    Example: Count from 1 to 5 using until

    count=1
    until [ $count -gt 5 ]
    do
      echo "Count: $count"
      ((count++))
    done
    

    Notice the key difference: until continues as long as the condition is false.

    Bash Loop Tips for Beginners

    • Use echo to debug your loop logic.
    • Prefer for loops for simple repetitions and lists.
    • Use while or until for more flexible conditions.
    • Don’t forget the do and done keywords!

    Is do while supported in Bash?

    No, Bash does not support do...while loops directly.

    In C/C++, a do...while loop runs at least once, and checks the condition after executing the block.

    // In C
    do {
        // code
    } while (condition);
    

    Workaround in Bash

    You can simulate a do...while loop in Bash using a while loop with a break condition inside.

    Bash Equivalent (Simulating do...while):

    count=1
    while true
    do
      echo "Count: $count"
      ((count++))
    
      if [ $count -gt 5 ]; then
        break
      fi
    done
    

    Here’s what’s happening:

    • while true ensures the loop runs at least once.
    • We manually check the condition after executing the body.
    • break exits the loop once the condition is met — mimicking do...while.

    Pro Tip

    If you must ensure a loop runs at least once in Bash, this pattern is your go-to replacement for do...while.

    FAQ | Learn How to Repeat Tasks Using for, while, and until Loops in Bash

    1. What is a loop in Bash?

    A loop in Bash is a control structure that allows you to repeat a task or a set of tasks multiple times without needing to write the same code over and over again. Loops are essential for automating repetitive tasks in your Bash scripts.

    2. What’s the difference between for, while, and until loops in Bash?

    • for loop: Executes a block of code for each item in a list or a range. Ideal when you know how many times you want the loop to run.
    • while loop: Runs as long as a given condition is true. Use this when the number of iterations isn’t fixed, but the condition should be true.
    • until loop: Similar to while, but it runs until the condition becomes true, meaning it stops when the condition is true.

    3. When should I use a for loop in Bash?

    Use a for loop when you want to iterate over a list or a range of numbers. It’s perfect for scenarios where you know the exact number of iterations you need.

    Example:

    for i in 1 2 3 4 5
    do
      echo "Iteration: $i"
    done
    

    4. How do while and until loops differ?

    • A while loop checks if a condition is true before executing the code block. It continues as long as the condition remains true.
    • An until loop works oppositely. It checks if a condition is false and will continue executing the block until the condition becomes true.

    Example while:

    count=1
    while [ $count -le 5 ]
    do
      echo "Count: $count"
      ((count++))
    done
    

    Example until:

    count=1
    until [ $count -gt 5 ]
    do
      echo "Count: $count"
      ((count++))
    done
    

    5. How can I stop an infinite loop in Bash?

    To prevent an infinite loop, make sure your loop condition will eventually become false. However, if you do find yourself in an infinite loop, you can stop it by pressing Ctrl+C in the terminal.

    6. Can I combine multiple loops in Bash?

    Yes, you can nest loops in Bash. This means you can place one loop inside another. Here’s an example with a for loop inside a while loop:

    count=1
    while [ $count -le 3 ]
    do
      for i in 1 2 3
      do
        echo "Count: $count, Inner loop: $i"
      done
      ((count++))
    done
    

    7. What is the syntax for for, while, and until loops?

    • for loop: for variable in list do # commands done
    • while loop: while [ condition ] do # commands done
    • until loop: until [ condition ] do # commands done

    8. Can I use a for loop with ranges in Bash?

    Yes, you can. Bash supports range iteration in for loops by specifying a range like this:

    for i in {1..5}
    do
      echo "Number: $i"
    done
    

    This will output numbers from 1 to 5.

    9. What happens if I forget the do or done in a loop?

    If you forget do or done, Bash will throw a syntax error. Both do and done are essential to mark the start and end of the code block inside loops.

    10. Can I break out of a loop in Bash?

    Yes! You can use the break command to exit a loop early, regardless of the loop condition.

    Example:

    for i in {1..10}
    do
      if [ $i -eq 5 ]; then
        break
      fi
      echo "Number: $i"
    done
    

    This will print numbers 1 to 4, then exit the loop when $i equals 5.

    11. What is the continue statement used for in loops?

    The continue statement skips the rest of the current iteration and moves to the next iteration of the loop.

    Example:

    for i in {1..5}
    do
      if [ $i -eq 3 ]; then
        continue
      fi
      echo "Number: $i"
    done
    

    This will skip the number 3 and print 1, 2, 4, 5

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @embedded-prep for contributing to this article on Embedded Prep

  • Master Shell script variables 2026

    Shell script variables are named memory locations used to store data that can be referenced and manipulated throughout a script. They allow scripts to be dynamic and flexible by holding values such as text, numbers, or the output of commands. Variables in shell scripts help reduce redundancy, enhance readability, and make automation tasks more efficient. They are defined without data types and can be easily modified during script execution, making them essential tools for controlling flow, configuring behavior, and managing data in Unix-based scripting environments.

    Shell script variables

    User-Defined Variables

    These are variables created by the user in a shell script or directly in the terminal.

    Syntax:

    variable_name=value

    No spaces around =.

    Example:

    name="Nish"
    echo "Hello, $name"

    Important Points:

    • Use $variable_name to access the value.
    • To avoid issues, wrap variables in double quotes ("$var").

    Environment Variables

    These are predefined variables provided by the system or the user for the shell environment.

    Used by the OS and shell to configure behavior.

    Examples:

    • HOME – your home directory
    • PATH – list of directories to search for commands
    • USER – your username
    • SHELL – path to the current shell

    Usage:

    echo $HOME
    echo $PATH

    Exporting a Variable:

    To make a user-defined variable available to child processes:

    export myvar="hello"

    Special Variables

    These are read-only shell variables that give info about the script or shell environment.

    VariableDescription
    $0Name of the script
    $1, $2...$nPositional parameters (arguments passed to the script)
    $#Number of arguments passed
    $@All arguments (as separate words)
    $*All arguments (as a single word)
    $$PID of the current shell
    $?Exit status of the last command
    $!PID of the last background process

    Example:

    #!/bin/bash
    echo "Script name: $0"
    echo "1st argument: $1"
    echo "Number of args: $#"

    Summary:

    TypeWho defines it?ScopeExample
    User-definedYouCurrent shellmyname="Nish"
    EnvironmentSystem/UserChild shells tooexport PATH
    SpecialShell itselfRead-only$0, $#, $?

    Shell Script: demo.sh

    #!/bin/bash
    
    # User-defined variable
    username="Nish"
    
    # Environment variable (export it so child processes can access it)
    export project="EmbeddedSystem"
    
    # Special variables
    echo "Script name        : $0"
    echo "1st argument        : $1"
    echo "2nd argument        : $2"
    echo "Total args passed   : $#"
    echo "All args (\$@)       : $@"
    echo "Current user        : $USER"
    echo "Home directory      : $HOME"
    echo "Custom username     : $username"
    echo "Project name        : $project"
    echo "Script PID          : $$"
    
    # Example of command and checking exit status
    ls /tmp
    echo "Exit status of 'ls' : $?"
    

    How to Run:

    chmod +x demo.sh
    ./demo.sh arg1 arg2

    Output Example:

    Script name        : ./demo.sh
    1st argument        : arg1
    2nd argument        : arg2
    Total args passed   : 2
    All args ($@)       : arg1 arg2
    Current user        : nish
    Home directory      : /home/nish
    Custom username     : Nish
    Project name        : EmbeddedSystem
    Script PID          : 12345
    Exit status of 'ls' : 0

    Frequently Asked Questions (FAQ)

    What is user input in Bash?

    User input in Bash refers to the data entered by a user while a script is running. This allows the script to behave dynamically based on the values provided at runtime.

    How do I take input from a user in a Bash script?

    You can use the read command. Example:

    read -p "Enter your name: " name
    echo "Hello, $name!"
    

    Can I hide user input in Bash (like for passwords)?

    Yes! Use the -s option with read to hide the input:

    read -sp "Enter your password: " password
    

    How do I set a default value if no input is provided?

    You can use parameter expansion:

    read -p "Enter your city [New York]: " city
    city=${city:-New York}
    echo "You chose $city"
    

    Can I read multiple inputs at once?

    Absolutely! Just list multiple variable names after read:

    read -p "Enter your first and last name: " first last
    echo "Welcome, $first $last!"
    

    Is user input available after the script ends?

    No. Bash variables—including those storing input—exist only while the script is running, unless you explicitly save them (e.g., in a file).

    How do I validate or restrict input?

    You can use conditional statements or loops to check input:

    while true; do
      read -p "Enter a number: " num
      [[ "$num" =~ ^[0-9]+$ ]] && break
      echo "Please enter a valid number."
    done
    

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • Bash User Input: Master How To read input in shell (2026)

    Bash User Input : Mastering user input in Bash is a fundamental skill every shell scripter must acquire. In this guide, we’ll walk through the modern and practical way of reading input from users using the read command in Bash, tailored for 2025 and beyond. Whether you’re automating system tasks, building CLI tools, or creating interactive scripts, handling user input effectively is key to writing dynamic and responsive scripts.

    We’ll cover everything from basic single-line inputs to advanced input handling—like silent password inputs, setting default values, and reading multiple variables at once. By the end of this tutorial, you’ll not only understand how to use read, but you’ll also be able to create scripts that interact intelligently with users.

    Designed with beginners in mind and polished with pro-level tricks, this guide ensures your journey from novice to Bash input master is smooth and efficient.

    Bash User Input :

    read Command Basics

    The read command is used to take input from the user.

    Syntax:

    read variable_name

    Example 1: Basic User Input

    #!/bin/bash
    
    echo "Enter your name:"
    read name
    echo "Hello, $name!"
    

    Example 2: Prompt Inline with -p

    #!/bin/bash
    
    read -p "Enter your favorite programming language: " language
    echo "Nice! You like $language."
    

    Example 3: Silent Input (useful for passwords) with -s

    #!/bin/bash
    
    read -sp "Enter your password: " password
    echo
    echo "Password entered."
    

    The input won’t be shown on screen while typing.

    Example 4: Reading Multiple Inputs

    #!/bin/bash
    
    read -p "Enter your first and last name: " fname lname
    echo "First Name: $fname"
    echo "Last Name : $lname"
    

    Example 5: Using Input in Conditions

    #!/bin/bash
    
    read -p "Are you sure you want to continue? (yes/no): " answer
    
    if [[ "$answer" == "yes" ]]; then
        echo "Continuing..."
    else
        echo "Exiting..."
    fi
    

    Summary of Useful read Options:

    OptionDescription
    -pPrompt inline before reading input
    -sSilent input (doesn’t echo input)
    -tTimeout for input in seconds
    -nLimit number of characters to read

    Awesome Nish! Here’s a complete interactive shell script that acts like a simple menu-driven program, taking input from the user and responding accordingly.

    Script: menu.sh

    #!/bin/bash
    
    echo "Welcome to the Interactive Menu Program!"
    echo
    
    while true; do
        echo "========== MENU =========="
        echo "1. Show current date and time"
        echo "2. Show current logged-in user"
        echo "3. List files in current directory"
        echo "4. Show system uptime"
        echo "5. Exit"
        echo "=========================="
        read -p "Enter your choice [1-5]: " choice
    
        case $choice in
            1)
                echo "📅 Date and Time: $(date)"
                ;;
            2)
                echo "👤 Logged-in User: $USER"
                ;;
            3)
                echo "📂 Files in $(pwd):"
                ls
                ;;
            4)
                echo "⏱️ System Uptime:"
                uptime
                ;;
            5)
                echo "Goodbye, $USER!"
                break
                ;;
            *)
                echo "❌ Invalid choice! Please select from 1 to 5."
                ;;
        esac
    
        echo
    done
    

    How to Run:

    1. Save as menu.sh
    2. Make it executable: chmod +x menu.sh
    3. Run it: ./menu.sh

    What it Demonstrates:

    • Taking input using read
    • Using case for control flow
    • Calling system commands (date, ls, uptime)
    • User interaction in a loop
    • Graceful exit

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • Master Bash if, else, and elif Statements (Beginner Friendly 2026)

    Bash if, else, and elif Statements : Are you ready to level up your shell scripting skills in 2025? This beginner-friendly guide is your one-stop solution to understanding decision-making in Bash using if, else, and elif statements.

    In this hands-on tutorial, you’ll explore the power of conditional logic in Bash scripting — the key to writing smarter, more dynamic scripts. We’ll walk you through:

    • The basic syntax of if, else, and elif blocks
    • Real-world use cases and examples you can actually use
    • Common mistakes beginners make and how to avoid them
    • Tips to make your conditional statements clean, efficient, and readable
    • Interactive practice scenarios to solidify your understanding

    Whether you’re automating tasks, building simple CLI tools, or just exploring Bash, mastering these control structures will help you script with confidence and precision.

    No prior experience? No worries. By the end of this tutorial, you’ll go from “what does elif even mean?” to confidently writing conditional logic like a pro.

    Let’s dive in and make your Bash scripts smarter in 2025!

    What Are Conditional Statements in Bash if , else?

    Conditional statements allow your script to make decisions. Bash checks if a condition is true or false, and runs different code based on that.

    Basic Syntax of if

    if [ condition ]
    then
        # commands to run if condition is true
    fi
    

    Example 1: Check If a File Exists

    #!/bin/bash
    
    filename="myfile.txt"
    
    if [ -f "$filename" ]
    then
        echo "✅ File '$filename' exists."
    else
        echo "❌ File '$filename' does not exist."
    fi
    

    Breakdown:

    • -f checks if the file exists and is a regular file.
    • If true, it prints the success message.
    • If false, the else part executes.

    Using elif for Multiple Conditions

    #!/bin/bash
    
    number=10
    
    if [ $number -lt 5 ]
    then
        echo "Number is less than 5"
    elif [ $number -eq 10 ]
    then
        echo "Number is exactly 10"
    else
        echo "Number is greater than 5 but not 10"
    fi
    

    Explanation:

    • -lt = less than
    • -eq = equal to

    Case 1: Disk Space Check

    #!/bin/bash
    
    disk_usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
    
    if [ $disk_usage -gt 90 ]
    then
        echo "⚠️ Warning: Disk usage is above 90%!"
    elif [ $disk_usage -gt 70 ]
    then
        echo "🟠 Caution: Disk usage is above 70%."
    else
        echo "✅ Disk usage is under control."
    fi
    

    This can help in setting up server monitoring scripts.

    Comparison Operators in Bash

    OperatorMeaning
    -eqEqual to (numbers)
    -neNot equal to
    -ltLess than
    -leLess than or equal
    -gtGreater than
    -geGreater than or equal

    String Comparison Operators

    OperatorMeaning
    =Equal
    !=Not equal
    <Less than (in ASCII order)
    >Greater than (ASCII)
    -zString is empty
    -nString is NOT empty

    Example:

    name="Nish"
    
    if [ "$name" = "Nish" ]
    then
        echo "Hello Nish!"
    fi
    

    Case 2: Weather-Based Message (Simulated)

    #!/bin/bash
    
    weather="rainy"
    
    if [ "$weather" = "sunny" ]
    then
        echo "Go for a walk!"
    elif [ "$weather" = "rainy" ]
    then
        echo "Carry an umbrella ☔"
    else
        echo "Check the forecast!"
    fi
    

    Tips for Writing if Statements in Bash

    1. Always use quotes around variables: "${var}" to avoid bugs.
    2. Spaces matter: [ "$a" = "$b" ] (correct) vs ["$a"="$b"] (wrong).
    3. Use [[ ]] if you’re doing complex conditionals or pattern matching.

    Nested if Statements

    #!/bin/bash
    
    age=20
    country="India"
    
    if [ $age -ge 18 ]
    then
        if [ "$country" = "India" ]
        then
            echo "You can vote in India!"
        else
            echo "You are eligible, but not in India."
        fi
    else
        echo "You're too young to vote."
    fi
    

    Case 3: Login Simulation

    #!/bin/bash
    
    username="admin"
    password="secret123"
    
    read -p "Enter username: " u
    read -sp "Enter password: " p
    echo
    
    if [ "$u" = "$username" ] && [ "$p" = "$password" ]
    then
        echo "✅ Login successful!"
    else
        echo "❌ Login failed!"
    fi
    

    BONUS: Using case for Clean Multiple Choices

    Sometimes if-elif becomes messy. case helps:

    #!/bin/bash
    
    read -p "Enter your choice (start/stop/restart): " action
    
    case "$action" in
        start) echo "Starting service...";;
        stop) echo "Stopping service...";;
        restart) echo "Restarting service...";;
        *) echo "Invalid choice.";;
    esac
    

    Summary

    KeywordUse Case
    ifStart a condition
    elseAlternative if condition is false
    elifCheck another condition if the first fails
    fiEnd the if block

    Practice Ideas

    1. Script to check if a user exists on your system.
    2. Temperature-based clothing suggestion script.
    3. Script that suggests meals based on time (morning, noon, evening).
    4. File backup script if size exceeds certain MB.

    Frequently Asked Questions (FAQ)

    Q1. What is the purpose of if, else, and elif in Bash?
    A: These are conditional statements that help your script make decisions. You can use them to run different blocks of code based on specific conditions, like checking if a file exists or comparing numbers.

    Q2. I’m completely new to Bash. Can I still follow this tutorial?
    A: Absolutely! This tutorial is designed with beginners in mind. We start from the very basics and gradually introduce more complex examples to help you build confidence step by step.

    Q3. What’s the difference between elif and else?
    A: elif (short for “else if”) lets you test additional conditions after an if statement. else runs only if none of the previous conditions are true. Think of elif as a second chance to test more logic before defaulting to else.

    Q4. Can I use these statements in loops or functions?
    A: Yes! if, else, and elif can be used inside loops and functions to add more logic and control flow to your scripts.

    Q5. How can I test my conditional Bash scripts safely?
    A: We recommend using the bash -x yourscript.sh command to debug and trace your script line by line. It helps you understand how your logic is executed.

    Q6. What should I do if my script gives a syntax error?
    A: Syntax errors are often caused by missing then, incorrect spacing, or forgetting to close blocks with fi. Don’t worry — we’ll show you the most common errors and how to fix them in the tutorial.

    Q7. Will this tutorial cover real examples?
    A: Yes! We include practical examples like checking user input, file existence, and numeric comparisons to help you apply what you learn in real-world scripts.

    Q8. Is this relevant for Linux users only?
    A: Bash is most commonly used in Linux and macOS, but Windows users can also follow along using WSL (Windows Subsystem for Linux) or Git Bash.

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • I – Interface Segregation Principle (ISP) | Master CPP Design Patterns 2026

    The Interface Segregation Principle (ISP) is one of the five SOLID principles of object-oriented programming. It states:

    “Clients should not be forced to depend on interfaces they do not use.“

    In simpler terms, this principle encourages splitting large, unwieldy interfaces into smaller, more specific ones so that classes only need to implement the methods they actually use.

    Why Interface Segregation Principle Matters

    When designing systems—especially in embedded software and large-scale applications—overly fat interfaces can lead to:

    • Unnecessary code coupling
    • Wasted resources in constrained systems
    • Fragile software that’s hard to maintain or extend

    By adhering to ISP, you build cleaner, modular, and more maintainable codebases that are easier to test and evolve over time.

    What does this mean (simple version)?

    Imagine an interface (abstract class) with 10 methods.
    You only need 2 of them — but you still have to override all 10, even the ones you don’t use. 😖

    That’s bad design.

    With ISP, you split that big interface into smaller purpose-specific ones so that:

    • Classes only implement what they really need
    • They’re not bloated with useless methods

    Real-Life Analogy

    Let’s say you go to a restaurant.

    If you only want coffee ☕, but they hand you a menu with 100 items, and ask you to order from every category — that’s overwhelming, right?

    Wouldn’t it be better if they had a “Drinks” menu separate from the “Meals” and “Desserts”?

    That’s Interface Segregation — keep things small, focused, and relevant.

    Bad Example: Violates ISP

    Let’s create a fat interface:

    class IMachine {
    public:
        virtual void print() = 0;
        virtual void scan() = 0;
        virtual void fax() = 0;
    };
    

    Now you want to create a basic printer:

    class OldPrinter : public IMachine {
    public:
        void print() override {
            cout << "Printing..." << endl;
        }
    
        void scan() override {
            // Not supported
            throw runtime_error("Scan not supported");
        }
    
        void fax() override {
            // Not supported
            throw runtime_error("Fax not supported");
        }
    };
    

    Problem:

    • You’re forced to implement scan() and fax() even if your class doesn’t need them.
    • You end up writing empty methods or throwing exceptions — that’s bad.

    Good Design: Follows ISP

    Split the large interface into smaller ones:

    class IPrinter {
    public:
        virtual void print() = 0;
    };
    
    class IScanner {
    public:
        virtual void scan() = 0;
    };
    
    class IFax {
    public:
        virtual void fax() = 0;
    };
    

    Now your classes only implement what they actually need:

    class BasicPrinter : public IPrinter {
    public:
        void print() override {
            cout << "Basic printing..." << endl;
        }
    };
    
    class MultiFunctionPrinter : public IPrinter, public IScanner, public IFax {
    public:
        void print() override {
            cout << "Printing..." << endl;
        }
    
        void scan() override {
            cout << "Scanning..." << endl;
        }
    
        void fax() override {
            cout << "Faxing..." << endl;
        }
    };
    

    Now:

    • BasicPrinter isn’t forced to implement scan() or fax()
    • Each class only depends on what it actually uses
    • You’re following the Interface Segregation Principle

    How to Spot ISP Violations

    SymptomIndicates ISP Violation?
    Class implements unused methods✅ Yes
    You throw exceptions in unused methods✅ Yes
    Interface has too many responsibilities✅ Yes
    Difficult to test one specific feature✅ Yes

    Tips to Apply ISP

    • 🔹 If your interface has more than 3–4 methods, check if it can be split.
    • 🔹 Group methods by behavior (e.g., Print, Scan, Fax = 3 separate concerns).
    • 🔹 Don’t overdo it — small, cohesive interfaces are ideal.
    • 🔹 Use multiple inheritance of interfaces for combining features in C++.

    ISP Mini Project Idea

    Let’s say you’re building a Vehicle system.

    Bad:

    class IVehicle {
    public:
        virtual void fly() = 0;
        virtual void drive() = 0;
        virtual void sail() = 0;
    };
    

    Now what if you just want to implement a car? 🚗

    Instead:

    class IDriveable {
    public:
        virtual void drive() = 0;
    };
    
    class IFlyable {
    public:
        virtual void fly() = 0;
    };
    
    class ISailable {
    public:
        virtual void sail() = 0;
    };
    

    Now a car only implements IDriveable, and a boat only implements ISailable.
    No more useless methods!

    Summary of ISP

    FeatureViolates ISP ❌Follows ISP ✅
    Big interfaces✅❌
    Empty/unimplemented methods✅❌
    Multiple small interfaces❌✅
    Clear class responsibilities❌✅

    Common Ground Between SRP and ISP

    They Both Want…Description
    🎯 Focused ResponsibilityBoth promote classes/interfaces doing only one thing
    🔍 Separation of ConcernsThey avoid overloaded classes or interfaces with too many duties
    💡 Clean & Maintainable CodeThey make systems modular, testable, and extendable

    Key Difference Between SRP and ISP

    PrincipleWhat it Applies ToFocusReal-World Example
    🟡 SRPClassesEach class should have one reason to changeA ReportGenerator class should only generate, not email or print
    🔵 ISPInterfacesClients should only depend on what they useA Printer class shouldn’t be forced to implement scan()

    In Summary:

    ➕ SRP:

    “Don’t overload classes with multiple jobs.”

    ➕ ISP:

    “Don’t overload interfaces with methods clients won’t use.”

    Think of it like:

    LayerSRP Deals WithISP Deals With
    Implementation“What this class is doing”“What this class is promising to do”
    Behavior Contracts—Interfaces

    Side-by-Side Quick Example

    // SRP
    class ReportGenerator {
    public:
        void generate() { /* ... */ }
    };
    // Bad: also sends emails or logs things
    
    // ISP
    class IPrinter {
    public:
        virtual void print() = 0;
    };
    
    class IScanner {
    public:
        virtual void scan() = 0;
    };
    // Good: only implement what you need
    

    Final Takeaway:

    🔧 SRP keeps classes focused.
    🔌 ISP keeps interfaces focused.

    They work hand-in-hand to make your code clean, modular, and friendly to change.

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • Adapter Design Pattern in C++ | Master CPP Design Patterns 2026

    Introduction of Adapter Design Pattern

    Adapter Design Pattern : When working on large software systems, you may come across situations where two incompatible interfaces need to work together. Instead of modifying existing code (which may be risky or impossible), you can use the Adapter Pattern to make them compatible.

    The Adapter Pattern acts like a bridge between two interfaces. It’s part of the Structural Design Patterns family in C++.

    What is the Adapter Design Pattern?

    The Adapter Design Pattern allows incompatible interfaces to work together by wrapping an existing class with a new interface.

    In simple terms, it’s like using a power plug adapter that lets a U.S. plug fit into a European socket.

    Real-life Analogy

    Imagine you have an old VGA cable that only works with projectors, but your laptop only has an HDMI port. You can’t plug it in directly. So, you use an HDMI-to-VGA adapter. Now your laptop can talk to the projector!

    That’s exactly what the Adapter Pattern does in code.

    Components of the Adapter Pattern

    1. Target – The expected interface (what your code is designed to use)
    2. Adaptee – The existing class that needs adapting
    3. Adapter – The class that wraps the Adaptee and makes it compatible with Target

    When to Use the Adapter Pattern

    • When you want to reuse an existing class, but its interface doesn’t match what you need.
    • When you want to decouple code from third-party libraries or legacy code.
    • When integrating new features into old systems.

    Adapter Pattern in C++ – Code Example

    Let’s look at a simple C++ example to understand how it works.

    Problem:

    We have an existing OldPrinter class. But our system expects printers to use the NewPrinterInterface.

    Step 1: Define the Target Interface

    // Target Interface
    class NewPrinterInterface {
    public:
        virtual void printDocument(const std::string& text) = 0;
        virtual ~NewPrinterInterface() {}
    };
    

    Step 2: Existing Class (Adaptee)

    // Adaptee
    class OldPrinter {
    public:
        void oldPrint(const std::string& text) {
            std::cout << "Old Printer Output: " << text << std::endl;
        }
    };
    

    Step 3: Create the Adapter

    // Adapter
    class PrinterAdapter : public NewPrinterInterface {
    private:
        OldPrinter* oldPrinter;
    
    public:
        PrinterAdapter(OldPrinter* printer) : oldPrinter(printer) {}
    
        void printDocument(const std::string& text) override {
            // Convert the request to match the old interface
            oldPrinter->oldPrint(text);
        }
    };
    

    Step 4: Client Code

    int main() {
        OldPrinter* legacyPrinter = new OldPrinter();
    
        // Use adapter to make it compatible with the new interface
        NewPrinterInterface* printer = new PrinterAdapter(legacyPrinter);
    
        printer->printDocument("Hello, Adapter Pattern!");
    
        delete printer;
        delete legacyPrinter;
    
        return 0;
    }
    

    Output

    Old Printer Output: Hello, Adapter Pattern!
    

    Success! The OldPrinter works with the new system using the adapter.

    Types of Adapters

    • Class Adapter (using inheritance): Inherit from both Target and Adaptee (only works in languages with multiple inheritance like C++).
    • Object Adapter (using composition): Use a reference to Adaptee inside the Adapter. This is more flexible and used in our example above.

    Benefits of Adapter Pattern

    • Promotes reusability of existing classes
    • Helps in code migration and integration
    • Makes your design more flexible and maintainable

    Drawbacks

    • Adds extra layers of code
    • May lead to performance overhead
    • Can make debugging slightly harder due to indirection

    Adapter vs. Other Patterns

    PatternPurpose
    AdapterMakes incompatible interfaces compatible
    DecoratorAdds behavior to an object dynamically
    BridgeDecouples abstraction from implementation
    FacadeProvides a simplified interface

    Conclusion

    The Adapter Pattern is a powerful tool in a C++ developer’s design toolbox. It allows your code to stay clean, extensible, and compatible with legacy or third-party systems. By wrapping old interfaces with new ones, you avoid rewriting or duplicating code.

    Use it wisely when integrating different systems — and you’ll be building robust and scalable software like a pro!

    FAQs – Adapter Pattern in C++

    Q1. Is Adapter Pattern a structural design pattern?
    Yes, it’s part of the Structural design patterns.

    Q2. Can we implement Adapter without inheritance?
    Yes! You can use composition (object adapter) for a more flexible solution.

    Q3. Is Adapter Pattern the same as a Wrapper?
    They are similar. A wrapper is a broader term; an adapter is a type of wrapper that converts interfaces.

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • Observer Pattern in Depth | Master CPP Design Patterns 2026

    What is the Observer Pattern?

    The Observer Pattern is a behavioral design pattern that defines a one-to-many relationship between objects. When one object (called the Subject) changes its state, all its dependents (called Observers) are notified and updated automatically.

    This pattern is commonly used when multiple parts of a system need to respond to changes in one part without tight coupling between components.

    Real-World Analogy

    Imagine you subscribe to a YouTube channel. When the creator uploads a new video, you get a notification.
    Here:

    • The YouTube channel is the Subject.
    • You (the viewer) are the Observer.
    • The notification is the update sent when the Subject changes (uploads new content).

    When to Use the Observer Pattern

    Use this pattern when:

    • An object should automatically notify other objects when its state changes.
    • You want to reduce tight coupling between classes.
    • You need to implement an event-driven system.

    Components of Observer Pattern

    1. Subject (Publisher)
      • Maintains a list of observers.
      • Provides methods to attach/detach observers.
      • Notifies observers when a change happens.
    2. Observer (Subscriber)
      • An interface or abstract class defining an update() method.
      • Concrete observers implement this method to respond to updates.
    3. ConcreteSubject
      • The actual implementation of the Subject.
      • When its state changes, it calls notify().
    4. ConcreteObserver
      • Implements the update() method to react to Subject’s state change.

    C++ Implementation

    Step-by-step example:

    1. Observer Interface

    class Observer {
    public:
        virtual void update(int newValue) = 0;
        virtual ~Observer() = default;
    };
    

    2. Subject Class

    #include <vector>
    
    class Subject {
    private:
        std::vector<Observer*> observers;
        int state;
    
    public:
        void attach(Observer* obs) {
            observers.push_back(obs);
        }
    
        void detach(Observer* obs) {
            observers.erase(
                std::remove(observers.begin(), observers.end(), obs),
                observers.end()
            );
        }
    
        void notify() {
            for (auto* obs : observers) {
                obs->update(state);
            }
        }
    
        void setState(int value) {
            state = value;
            notify();
        }
    
        int getState() const {
            return state;
        }
    };
    

    3. Concrete Observers

    #include <iostream>
    
    class ConcreteObserver : public Observer {
    private:
        std::string name;
    
    public:
        ConcreteObserver(const std::string& n) : name(n) {}
    
        void update(int newValue) override {
            std::cout << name << " received update: " << newValue << std::endl;
        }
    };
    

    4. Main Function

    int main() {
        Subject subject;
    
        ConcreteObserver obs1("Observer A");
        ConcreteObserver obs2("Observer B");
    
        subject.attach(&obs1);
        subject.attach(&obs2);
    
        subject.setState(42); // Both observers get notified
    
        subject.detach(&obs1);
    
        subject.setState(99); // Only Observer B gets notified
    
        return 0;
    }
    

    Advantages

    • Loose coupling between subject and observers.
    • Easy to add or remove observers at runtime.
    • Supports broadcast communication (one-to-many).

    Disadvantages

    • Can cause performance issues if there are many observers.
    • Debugging can become tricky due to complex dependencies.
    • Risk of memory leaks if observers aren’t properly removed.

    Quick Summary

    ComponentRole
    SubjectMaintains and notifies observers
    ObserverReceives and responds to updates
    ConcreteSubjectHolds the actual state
    ConcreteObserverImplements response behavior

    The Observer Pattern is perfect when multiple components need to stay in sync with a single source of truth .

    Goal: Notify users when a new video is uploaded.

    Without Observer Pattern

    In this case, the YouTube channel manually knows about each user and directly notifies them. It becomes tightly coupled — hard to manage when users increase.

    Code:

    #include <iostream>
    #include <string>
    
    class User {
        std::string name;
    public:
        User(const std::string& name) : name(name) {}
        void notify(const std::string& videoTitle) {
            std::cout << name << " received: " << videoTitle << "\n";
        }
    };
    
    class YouTubeChannel {
        User* user1;
        User* user2;  // tightly coupled to specific users
    public:
        YouTubeChannel(User* u1, User* u2) : user1(u1), user2(u2) {}
    
        void uploadVideo(const std::string& title) {
            std::cout << "New video uploaded: " << title << "\n";
            user1->notify(title);
            user2->notify(title);
        }
    };
    
    int main() {
        User nish("Raj");
        User khushi("Mukesh");
        YouTubeChannel channel(&nish, &khushi);
        channel.uploadVideo("C++ Basics");
        return 0;
    }
    

    Problem:

    • Channel must know all users.
    • Adding/removing users needs code change.
    • Tight coupling = not flexible.

    With Observer Pattern

    Now, we decouple the YouTubeChannel and Users using the Observer Pattern.

    Code:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    
    // Observer interface
    class Subscriber {
    public:
        virtual void update(const std::string& videoTitle) = 0;
    };
    
    // Concrete Observer
    class User : public Subscriber {
        std::string name;
    public:
        User(const std::string& name) : name(name) {}
        void update(const std::string& videoTitle) override {
            std::cout << name << " received: " << videoTitle << "\n";
        }
    };
    
    // Subject
    class YouTubeChannel {
        std::vector<Subscriber*> subscribers;
    public:
        void subscribe(Subscriber* s) {
            subscribers.push_back(s);
        }
    
        void unsubscribe(Subscriber* s) {
            subscribers.erase(
                std::remove(subscribers.begin(), subscribers.end(), s),
                subscribers.end()
            );
        }
    
        void uploadVideo(const std::string& title) {
            std::cout << "New video uploaded: " << title << "\n";
            for (Subscriber* s : subscribers) {
                s->update(title);
            }
        }
    };
    
    int main() {
        YouTubeChannel channel;
    
        User nish("Raj");
        User khushi("Mukesh");
    
        channel.subscribe(&nish);
        channel.subscribe(&khushi);
    
        channel.uploadVideo("Observer Pattern in C++");
    
        channel.unsubscribe(&khushi);
    
        channel.uploadVideo("Advanced C++ Design Patterns");
    
        return 0;
    }
    

    Benefits:

    • YouTubeChannel is not tied to any specific users.
    • Users can dynamically subscribe/unsubscribe.
    • Easy to extend — you can have any number of subscribers.

    Difference between Without Observer Pattern and Observer Pattern

    FeatureWithout Observer PatternWith Observer Pattern
    CouplingTightly coupledLoosely coupled
    Adding/removing observersHard, manualEasy, dynamic
    ScalabilityPoorExcellent
    ReusabilityLowHigh

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on Embedded Prep

  • What is Shell Scripting? Master Complete Beginner’s Guide with Examples (2026)

    What is Shell Scripting : Shell scripting is a powerful way to automate tasks in Unix/Linux systems using command-line instructions written in a script file. Whether you’re a beginner or just curious about how things work behind the terminal, this guide will walk you through what shell scripting is, why it’s useful, and how to write your very first script – all with real-world examples!

    In this complete beginner’s guide, you’ll learn:

    • What shell scripting is and why it matters
    • Different types of shells like Bash, Zsh, and Sh
    • How to create and run your first shell script
    • Practical examples like file handling, loops, conditionals, and functions
    • Tips for writing clean, efficient scripts

    By the end of this tutorial, you’ll be able to automate repetitive tasks, schedule jobs with cron, and write scripts that make your Linux experience much more productive .

    Shell Scripting Syllabus

    S.NoTitleTopic
    1What is Shell Scripting? A Complete Beginner’s GuideLearn what Shell scripting is, how it works, and why it’s essential for automation.
    2How to Write Your First Shell Script [Step-by-Step]Hands-on guide to writing and executing your first Bash script.
    3Shell Variables Explained with ExamplesUnderstand Shell script variables including user-defined, environment, and special.
    4User Input in Shell Scripts [Read Command]Learn how to accept and handle user input in interactive shell scripts.
    5Conditional Statements in Shell Scripts (If-Else, Elif)Master Bash if, else, and elif statements with practical examples.
    6Loops in Shell Scripting (For, While, Until)Learn how to repeat tasks using for, while, and until loops in Bash.
    7Functions in Shell Scripting [Modular Code]Organize your scripts with reusable functions and return values.
    8Command Line Arguments ($1, $2…)Use and manage positional parameters in scripts like $1, $2, $#, and $@.
    9Shell Script Debugging TechniquesDebug like a pro using set -x, trap, and best practices.
    10Working with Files and Directories in ShellAutomate file creation, movement, deletion, and permissions via scripting.
    11Shell Script Automation Projects [Real Examples]Implement real-world automation projects using Bash scripts.
    12Cron Jobs and Shell Script SchedulingSchedule your scripts using crontab and automate recurring tasks.
    13Error Handling in Shell ScriptsHandle errors gracefully using exit codes, trap, and logging.
    14Regular Expressions in Shell ScriptsUse grep, sed, and awk with regex for powerful text processing.
    15Shell Scripting Best Practices for Production UseWrite efficient, maintainable, and secure production-ready scripts.
    16Arrays in Shell ScriptsLearn how to use arrays in bash with loops and manipulation examples.
    17String Operations in Shell ScriptsPerform advanced string operations like slicing, replacing, and comparison.
    18Date and Time Operations in Shell ScriptsUse date/time functions to timestamp logs or schedule actions.
    19File Testing in Shell Scripts (-f, -d, -e)Test file types and existence to create intelligent scripts.
    20Shell Script Exit Status & Return CodesUnderstand and use exit codes for debugging and control flow.
    21Logging in Shell Scripts (Log Files + Timestamps)Create and manage logs with dynamic file names and timestamps.
    22Reading and Writing to Files with Shell ScriptingLearn to read from and write to text/log/config files in Bash.
    23Case Statement in Shell ScriptsSimplify multi-condition logic using case…esac.
    24Trap Command in Shell ScriptingUse trap to handle unexpected script exits and clean up processes.
    25Signals and Process Management in ShellWork with process IDs, kill signals, and background jobs.

    Beginner Level

    1. What is Shell Scripting? A Complete Beginner’s Guide

    • Learn what Shell scripting is, how it works, and why it’s essential for automating tasks in Linux/Unix. Perfect guide for beginners.
    • Sections:
      • What is a Shell?
      • Types of Shells (Bash, Zsh, Ksh, etc.)
      • Why use Shell Scripting?
      • Hello World Example
      • Running a Shell Script

    2. How to Write Your First Shell Script [Step-by-Step]

    • Start your shell scripting journey with this hands-on guide to writing your first script. Ideal for beginners!
    • Topics:
      • File creation and permissions (chmod)
      • Shebang (#!/bin/bash)
      • Echo, variables, comments
      • Running a script from the terminal

    3. Shell Variables Explained with Examples

    • Understand Shell script variables with real-world examples. Learn user-defined, environment, and special variables.
    • Topics:
      • Declaring variables
      • Environment variables
      • $0, $1, $?, etc.
      • Using export

    4. User Input in Shell Scripts [Read Command]

    • Learn how to accept user input in shell scripts using the read command. Build interactive scripts.
    • Topics:
      • read with one and multiple inputs
      • -p and -s flags
      • Input validation basics

    5. Conditional Statements in Shell Scripts (If-Else, Elif)

    • Master conditional logic in Shell scripting with if, else, elif blocks and test expressions.
    • Topics:
      • if, else, elif
      • test, [ ], [[ ]] syntax
      • String and number comparisons

    Intermediate Level

    6. Loops in Shell Scripting (For, While, Until)

    • Learn how to use for, while, and until loops to repeat tasks in shell scripts effectively.
    • Topics:
      • Loop syntax
      • Looping through arrays/files
      • Infinite loops and breaks

    7. Functions in Shell Scripting [Modular Code]

    • Make your scripts modular using functions. Learn function definition, scope, return values, and arguments.
    • Topics:
      • Declaring and calling functions
      • Local vs global variables
      • Return statements

    8. Command Line Arguments ($1, $2…)

    • Learn how to use command-line arguments in shell scripts. Create flexible and reusable scripts.
    • Topics:
      • Accessing arguments with $1, $2, etc.
      • $#, $@, $*, $?
      • Argument count check

    9. Shell Script Debugging Techniques

    • Debug your shell scripts like a pro. Learn to use set -x, echo, trap, and more.
    • Topics:
      • set -x, set -e, trap
      • Common syntax and logic errors

    10. Working with Files and Directories

    • Automate file management with Shell scripting. Learn file creation, deletion, searching, and permissions.
    • Topics:
      • touch, rm, mv, cp
      • find, grep, cut, awk
      • File permissions (chmod, chown)

    Advanced Level

    11. Shell Script Automation Projects

    • Explore real-world shell script projects to automate backups, log analysis, and cron jobs.
    • Ideas:
      • Backup automation with tar
      • Server health check script
      • Log parsing script

    12. Cron Jobs and Shell Script Scheduling

    • Schedule your shell scripts using cron jobs. Automate routine tasks with precision.
    • Topics:
      • crontab syntax
      • Cron expressions (*/5 * * * *)
      • Logging cron jobs

    13. Error Handling in Shell Scripts

    • Learn best practices for error handling in Bash. Master exit codes, traps, and custom error messages.
    • Topics:
      • Exit codes and trap
      • Using ||, &&
      • Custom error logging

    14. Regular Expressions in Shell Scripts

    • Use powerful regex with grep, sed, and awk to filter and transform text in shell scripts.
    • Topics:
      • grep, sed, awk with regex
      • Extracting data from logs

    15. Shell Scripting Best Practices for Production Use

    • Write secure, maintainable, and efficient shell scripts for real-world automation.
    • Topics:
      • Code structure and comments
      • Input validation and sanitization
      • Logging and monitoring

    Shell Scripting Interview Questions

    LevelInterview QuestionFollow-Up Questions
    BasicWhat is a shell? What is shell scripting?Which shell do you use most often and why? Name some popular shells in Linux.
    How do you create a shell script?What file extension is commonly used for shell scripts?
    How do you execute a shell script?What’s the difference between sh script.sh and ./script.sh?
    What is #!/bin/bash?Why is the shebang important in shell scripts?
    What are shell variables?What’s the difference between local and environment variables?
    How do you take user input in a shell script?How do you validate user input (e.g., numeric only)?
    How are conditional statements used in Bash?What is the difference between if, elif, and case statements?
    What are loops in shell scripting?When would you use while vs for loop?
    What are positional parameters?What do $0, $#, $@, and $* mean in Bash?
    How do you make a script executable?Can you change file permissions using a script?
    LevelInterview QuestionFollow-Up Questions
    IntermediateHow do you pass arguments to a shell script?How do you handle default arguments if none are passed?
    What is the use of read command in Bash?How do you make read silent (like password input)?
    How do you handle errors in shell scripts?What does set -e or trap do in a script?
    What is the difference between " and ' in Bash?How do you handle variable expansion inside strings?
    How do you redirect output in Bash?What’s the difference between > and >>? How about 2>&1?
    What are arrays in Bash?How do you iterate over an array?
    What is the difference between exec and eval?When should you use eval with caution?
    What is the trap command used for?Can you use trap to clean up temporary files?
    How do you comment multiple lines in a shell script?Can : (colon) be used as a comment placeholder?
    How do you schedule scripts using cron?Where is the cron log file located? What is the crontab -e used for?
    LevelInterview QuestionFollow-Up Questions
    AdvancedHow do you handle parallel processing in a shell script?How do you ensure background processes finish before script exits?
    How do you debug a complex shell script?What is set -x? How do you selectively debug sections?
    What are subshells in Bash?What is the difference between () and {} in command grouping?
    How do you parse JSON or CSV in a shell script?Which tools would you use (jq, awk, cut, etc.) and why?
    How do you write a script to monitor CPU or memory usage?Can you automate alerts using email or logs in the same script?
    What is the use of here document (<<) in shell scripting?What’s the difference between <<EOF and <<-EOF?
    How do you integrate shell scripts with other languages (Python, C, etc.)?Can you call APIs or external services from Bash? How?
    How would you manage large shell scripts (modularization)?How do you include/import functions from another script file?
    What is the difference between source script.sh and ./script.sh?How does environment sharing differ in both cases?
    How do you ensure shell scripts are portable across different Linux distros?What best practices do you follow for compatibility?

    FAQ | What is Shell Scripting

    1. What is Shell Scripting?

    Shell scripting is the process of writing a series of commands for the shell (the command-line interface) to execute. It allows automation of tasks, simplifying system administration, and enhances workflow efficiency. Shell scripts are typically written in shell programming languages like Bash, Zsh, or other Unix shells.

    2. Why should I learn Shell Scripting?

    Learning shell scripting is essential for automating repetitive tasks, managing system operations, and enhancing productivity. It’s especially useful for system administrators, DevOps engineers, and software developers to streamline their workflows and make their systems more efficient.

    3. What are the prerequisites for learning Shell Scripting?

    A basic understanding of command-line interfaces (CLI) and Linux/Unix systems is recommended. Familiarity with navigating the terminal and running simple commands will make it easier to learn shell scripting.

    4. Which shell programming language should I use?

    The most commonly used shell for scripting is Bash (Bourne Again Shell), but other shells like Zsh, Fish, or Ksh can also be used. For beginners, Bash is widely recommended due to its popularity and extensive documentation.

    5. Can I run Shell Scripts on Windows?

    Yes, you can run shell scripts on Windows using tools like Git Bash, Windows Subsystem for Linux (WSL), or Cygwin. These provide a Unix-like environment for running shell scripts.

    6. What are the main benefits of using Shell Scripts?

    • Automation: Automates tasks like backups, system updates, and software installation.
    • Efficiency: Speeds up the execution of multiple commands or processes.
    • Portability: Shell scripts are portable across different Unix-like systems, making them ideal for cross-platform automation.
    • Customization: Allows you to tailor scripts for specific tasks and systems.

    7. Can I use Shell Scripts for programming?

    While shell scripts are not typically used for application development like Python or C++, they are excellent for tasks like file manipulation, text processing, and system monitoring. They complement other programming languages by handling system-level tasks.

    8. How do I start writing a Shell Script?

    To start writing a shell script:

    1. Open a text editor and write the script using basic shell commands.
    2. Save the script with a .sh extension (e.g., myscript.sh).
    3. Give execute permissions using the command chmod +x myscript.sh.
    4. Run the script with ./myscript.sh.

    9. What are some common examples of Shell Scripts?

    • Backup scripts: Automate file and directory backups.
    • System monitoring scripts: Track disk usage, CPU load, or memory consumption.
    • Batch processing scripts: Process multiple files or directories in one go.

    10. How can I make my Shell Scripts more efficient?

    To improve your shell scripts:

    • Use functions to avoid repetition.
    • Validate inputs and check for errors.
    • Write comments to explain the code for easier understanding.
    • Use loops and conditionals for automation.
    • Optimize commands to minimize system resource usage.

    11. What are variables in Shell Scripting?

    Variables are used to store data that can be referenced and modified throughout the script. They can store strings, numbers, or outputs from commands.

    12. How do I debug a Shell Script?

    You can debug your shell script by running it with the -x option to trace its execution. Use echo statements to print variable values and outputs at different points in the script.

    13. Are there any advanced topics in Shell Scripting?

    Yes, advanced topics include:

    • Working with regular expressions.
    • Using loops and conditionals for complex logic.
    • Error handling and logging.
    • Creating functions for modular scripts.
    • Interfacing with other programming languages or APIs.

    14. Can I use Shell Scripting with other programming languages?

    Yes, shell scripts can be used in conjunction with other programming languages. For instance, you can call Python or C++ programs from a shell script, or use shell scripts to manage and automate tasks related to these programs.

    15. How long will it take to master Shell Scripting?

    The learning curve for shell scripting depends on your experience with command-line interfaces. Basic scripting can be learned within a few days, while mastering more complex scripts may take weeks of practice and real-world application.

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @embedded-prep for contributing to this article on Embedded Prep

  • Startup Code Explained: 5 Critical Things You Must Know in Embedded Systems (2026)

    Understand embedded startup code in depth from reset handlers to memory initialization with 5 critical insights engineers must know. Perfect for beginners and pros alike!

    What is Startup Code? | Beginner-Friendly Guide

    Startup code is a small but essential piece of code that runs before your main program starts. It prepares your system so that your program can run smoothly. This is especially important in embedded systems and low-level programming.

    Simple Definition :

    Startup code is the very first code that executes when a device is powered on or reset. It sets up the environment so that the main() function can run properly.

    Why is Startup Code Important?

    Think of startup code as a stage crew in a play. Before the actors come on stage, the crew sets the lighting, positions the props, and makes sure everything is ready. Similarly, startup code:

    • Sets up memory sections like .data, .bss, and stack
    • Initializes hardware components if needed
    • Calls constructors (in C++)
    • Finally, it jumps to the main() function

    What Does Startup Code Typically Do?

    1. Set the Stack Pointer
      Sets the stack to a known location in RAM.
    2. Initialize Data Section
      Copies initialized global variables from Flash to RAM.
    3. Zero out the BSS Section
      Sets all uninitialized global and static variables to zero.
    4. Call System Initialization Code
      Optional hardware-specific setup.
    5. Call main() Function
      After everything is set up, control is passed to your program’s main() function.

    Where is Startup Code Found?

    • In embedded systems, startup code is usually written in assembly or C.
    • It is provided by the compiler toolchain (like GCC, Keil, etc.) or custom-written for a specific microcontroller.

    Conclusion

    Startup code may not be visible when writing high-level programs, but it plays a crucial role behind the scenes. Especially in embedded development, understanding how startup code works can help you debug issues, customize behavior, and build better systems.

    u can also Visit other tutorials of Embedded Prep 

    Special thanks to @embedded-prep for contributing to this article on Embedded Prep

  • Master Counters in Embedded Systems (2026)

    Explore Master Counters in Embedded Systems (2026): essential concepts, timers vs counters, interrupt integration, performance tips, and hands-on examples for microcontrollers.

    Counters in Embedded Systems : Embedded systems form the backbone of many modern devices, from smartphones to household appliances. One crucial component of these systems is the counter. In this guide, we will explore the concept of counters in embedded systems, their purpose, how they work, and their practical applications in the real world.

    What is a Counter in an Embedded System?

    A counter in an embedded system is a digital component used to keep track of events, actions, or time. It’s essentially a variable that increments or decrements with every event, pulse, or clock cycle. Counters are widely used in embedded systems for a variety of purposes such as timekeeping, event counting, frequency measurement, and more.

    Why Are Counters Important in Embedded Systems?

    Counters are essential because they allow embedded systems to track and manage data over time. For instance, counters are commonly used to:

    • Measure time: Keep track of time intervals for scheduling tasks.
    • Count events: Record the number of occurrences of specific events.
    • Control hardware: Control the state of various hardware components based on the counter’s value.

    Types of Counters in Embedded Systems

    There are several types of counters used in embedded systems, and each serves a different function. Let’s take a look at the main types:

    1. Binary Counter

    A binary counter is the most common type of counter in embedded systems. It stores the count in a binary format and increments or decrements the value with every clock cycle. The counter can be either up (incrementing) or down (decrementing).

    Example: A binary counter could be used to count the number of button presses.

    2. Decade Counter

    A decade counter counts from 0 to 9 and then resets to 0. It is useful in applications where you need to count events in decimal format.

    Example: A digital clock uses a decade counter to count seconds.

    3. Up/Down Counter

    An up/down counter can increment or decrement based on an external control signal. These counters are highly versatile because they can count both forward and backward.

    Example: A counter in a reverse parking sensor can count the distance traveled backward.

    4. Programmable Counter

    A programmable counter allows you to set a specific threshold or count value at which it will reset or trigger an interrupt. These counters are flexible and can be adapted to different needs in embedded applications.

    Example: A traffic light controller can use a programmable counter to time the switching of lights.

    How Do Counters Work in Embedded Systems?

    Counters typically work by utilizing a clock signal. The clock is a timing signal that determines when the counter should change its value. This can be a periodic signal, such as a timer interrupt, or an event-based signal, like a button press.

    Here’s a basic explanation of how a counter works:

    1. Initialization: The counter is initialized to a starting value (typically 0).
    2. Clock or Event Trigger: Every time the clock signal or a specific event occurs, the counter either increments or decrements.
    3. Action at Threshold: If the counter reaches a predefined threshold, it can trigger an action like resetting the count, generating an interrupt, or performing a task.

    Practical Applications of Counters in Embedded Systems

    Counters have widespread usage in embedded systems. Some common practical applications include:

    1. Timers and Clocks

    In microcontrollers, counters are used to create accurate timers and clocks for controlling when specific actions occur. For example, a counter could be used to generate periodic interrupts for scheduling tasks in an RTOS (Real-Time Operating System).

    2. Event Counting

    Counters are used in applications where you need to count events, such as the number of items passed on a conveyor belt in an industrial process. This can help in quality control, production tracking, and automation.

    3. Frequency Measurement

    Counters are essential in measuring the frequency of an incoming signal. For example, an embedded system in a radio receiver can use a counter to determine the frequency of the received signal and process it accordingly.

    4. PWM Generation

    Pulse Width Modulation (PWM) signals are often generated using counters. The counter helps in controlling the duty cycle of the PWM signal to regulate the power delivered to devices like motors, LEDs, and heating elements.

    5. Digital Clocks

    Digital clocks are often implemented using decade counters, which are simple and efficient. The counters keep track of seconds, minutes, and hours, displaying them in the correct format.

    Programming Counters in Embedded Systems

    Counters in embedded systems can be programmed using low-level languages like C or assembly. Here’s a simple example of a counter in C for an embedded system:

    #include <stdio.h>
    
    volatile int counter = 0;  // Declaring counter as volatile to ensure it's updated
    
    void timer_interrupt_handler() {
        counter++;  // Increment counter every time the timer interrupt is triggered
        if (counter == 1000) {
            printf("Counter reached 1000!\n");
            counter = 0;  // Reset counter
        }
    }
    
    int main() {
        // Setup timer interrupt
        setup_timer_interrupt(timer_interrupt_handler);
    
        while(1) {
            // Main loop
        }
        
        return 0;
    }
    

    In this example, the counter increments every time the timer interrupt is triggered. Once the counter reaches 1000, it prints a message and resets.

    The 8051 microcontroller has two 16-bit Timer/Counter modules:

    • Timer 0
    • Timer 1

    Both can operate as either:

    • Timers (based on the internal clock)
    • Counters (based on external events)

    These modules are incredibly useful in embedded systems for:

    • Time delays
    • Event counting
    • Pulse generation
    • Measuring signal durations
    • Frequency measurements

    Let’s understand each mode deeply.

    Timer Mode (Internal Time Counting)

    What is Timer Mode?

    In Timer mode, the microcontroller uses its internal oscillator clock to increment the timer register. The CPU doesn’t do this manually — the timer is hardware-controlled and works independently.

    For example: If your microcontroller has a 12 MHz clock, then each machine cycle = 1 µs (8051 takes 12 clock cycles for one machine cycle).
    So, if the timer counts from 0 to 255 (8-bit), it takes:

    256 × 1 µs = 256 µs
    

    before it overflows.

    This overflow can trigger an interrupt, which can be used to perform specific actions like toggling an LED, reading a sensor, etc.

    Use Cases of Timer Mode:

    • Creating accurate time delays.
    • Generating baud rate for serial communication.
    • Time-based task scheduling.
    • Creating software timers for multitasking.

    Counter Mode (External Event Counting)

    What is Counter Mode?

    In Counter mode, the timer is not counting time — instead, it is counting external events or pulses.

    These events are fed through the T0 (P3.4) or T1 (P3.5) pins, depending on whether you’re using Timer 0 or Timer 1.

    Example:

    Suppose you have a sensor that outputs a pulse every time a person enters a room. You can connect that pulse to pin P3.4 (T0) and configure Timer 0 as a counter. The 8051 will increment the counter register every time it detects a falling or rising edge (depending on configuration).

    Use Cases of Counter Mode:

    • Counting number of items on a conveyor.
    • Counting external signal pulses.
    • Measuring frequency of a waveform.
    • Speed sensors or encoder-based measurements.

    Configuration: TMOD Register

    The TMOD (Timer Mode) Register is an 8-bit register used to configure the mode of operation for Timer 0 and Timer 1.

    TMOD Format:

    | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
       T1     T1   T1   T1    T0     T0   T0   T0
    
    • GATE: When set, timer is controlled by an external pin. Usually set to 0 for software start/stop.
    • C/T (Counter/Timer Select):
      • 0: Timer Mode (uses internal clock)
      • 1: Counter Mode (uses external pulse input)
    • M1, M0: Select the mode of operation
      • 00 – Mode 0 (13-bit Timer)
      • 01 – Mode 1 (16-bit Timer)
      • 10 – Mode 2 (8-bit auto-reload)
      • 11 – Mode 3 (split timer mode or unused depending on timer)

    Example:

    To configure Timer 0 in Counter Mode, Mode 1 (16-bit):

    TMOD = 0x05;  // 00000101 in binary
    
    • Lower nibble (00000101):
      • GATE = 0 (ignore external control)
      • C/T = 1 (Counter Mode)
      • M1 M0 = 0 1 (Mode 1 → 16-bit)

    Key Differences Between Timer and Counter

    FeatureTimer ModeCounter Mode
    Clock SourceInternal clockExternal input pin (T0/T1)
    Use CaseDelay generation, scheduling tasksCounting pulses, measuring frequency
    TMOD C/T Bit01
    Controlled BySystem ClockExternal hardware signal
    Pins UsedNone (internal operation)T0 (P3.4), T1 (P3.5)
    Typical ApplicationsDelay loops, baud rate genEvent counters, frequency meter

    Practical Tip for Beginners

    • If you’re building a digital stopwatch, use Timer mode.
    • If you’re counting how many people passed through a door using a sensor, use Counter mode.
    • Always set the TMOD register carefully — most bugs come from misconfiguring this!

    How to Start in Keil (for Practice)

    You can try this code to configure Timer 0 in Counter mode and read its value:

    #include <reg51.h>
    
    void main() {
        TMOD = 0x05;     // Timer 0 as Counter, Mode 1 (16-bit)
        TL0 = 0x00;      // Clear low byte
        TH0 = 0x00;      // Clear high byte
        TR0 = 1;         // Start the counter
    
        while(1) {
            // Continuously check counter value
            // Display TL0, TH0 on LCD or Serial Monitor for observation
        }
    }
    

    Connect a pulse generator or button to P3.4 (T0 pin), and every press or pulse will increase the count.

    ConceptSummary
    Timer ModeCounts time using internal clock
    Counter ModeCounts events from outside sources
    TMODConfigures mode and type (timer/counter)
    T0, T1 PinsUsed in Counter mode to receive pulses
    Mode TypesMode 0 (13-bit), Mode 1 (16-bit), Mode 2 (8-bit auto-reload), Mode 3 (split)

    Conclusion

    Counters are a fundamental building block in embedded systems, enabling precise timekeeping, event tracking, and hardware control. Whether you’re measuring time, counting events, or generating signals, counters play a vital role in ensuring your embedded system performs its tasks effectively. By understanding how counters work and how to program them, you can enhance your embedded systems’ capabilities and achieve better control over your projects .

    Interview Questions on Counters in Embedded Systems

    Basic to Intermediate Questions

    1. What distinguishes a timer from a counter at the hardware level in microcontrollers like 8051?
    2. Why does a counter in 8051 count on falling edges instead of rising edges of external input signals?
    3. What is the role of the C/T bit in the TMOD register, and how does it influence timer vs counter operation?
    4. Why is Mode 2 in 8051 often referred to as the “auto-reload” mode, and what are its typical use cases?
    5. How does the THx and TLx register pair work together in 8051 timer/counter operations?
    6. How does using an external crystal oscillator (e.g., 11.0592 MHz) affect the accuracy of timing calculations?
    7. Can Timer 1 operate in Counter Mode while Timer 0 operates in Timer Mode in 8051? Explain how.
    8. Why is overflow handling critical in counter-based applications, and how can interrupts help?
    9. What is the maximum frequency that can be reliably counted using Timer 0 in Counter Mode?
    10. How do you ensure glitch-free pulse counting on external inputs in a noisy environment?

    Advanced/Scenario-Based Questions

    1. Suppose you want to measure the RPM of a motor shaft using a sensor. How would you use 8051’s counter mode for that?
    2. If Timer 1 is running in Mode 1 and Timer 0 is in Mode 2 (Counter Mode), how would their operations differ?
    3. You have a push-button input connected to a counter pin. How would you prevent false triggering due to switch bounce?
    4. What are the limitations of 8051 timers in applications requiring long-duration delays? How can you overcome them?
    5. Describe a real-world use case where GATE-controlled counting is beneficial.

    Practical/Code-Based Questions

    1. Write a code snippet in Embedded C to initialize Timer 0 in Counter Mode for counting external pulses up to 255.
    2. Develop a function in C that waits until Timer 0 overflows, then resets it and continues.
    3. Implement a C code logic to count 1000 pulses on T0 pin and light up an LED once the count is reached.
    4. Write ISR (Interrupt Service Routine) code for handling Timer 0 overflow in Counter Mode.
    5. Simulate an event logger using Timer 1 in Counter Mode that timestamps each pulse input.

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @embedded-prep for contributing to this article on Embedded Prep