When you open a terminal on a Linux machine and type a command, something has to read what you typed, understand it, and tell the operating system what to do. That something is the **shell**. The shell is a program that sits between you and the operating system. You type human-readable commands into it. It interprets them and passes them to the kernel — the core of the operating system — which actually talks to the hardware. ### Terminal, Shell, and Kernel These three terms get confused constantly. Here is what each one actually does: **Terminal** is the window you type into. It is just an interface — it accepts your input and displays output. It cannot understand commands on its own. It passes everything to the shell. **Shell** is the interpreter. It reads what you type, checks if it is valid, and converts it into instructions the kernel can execute. It is also a programming environment — you can write scripts, use variables, loops, and functions. **Kernel** is the core of the operating system. It manages memory, processes, files, and hardware. The shell talks to the kernel through system calls. You never interact with the kernel directly. ``` You type a command in the Terminal ↓ Shell reads it, validates it, translates it ↓ Kernel receives the instruction ↓ Hardware executes it ↓ Result comes back through the same path ↓ Terminal displays the output ``` ### Types of Shells Linux supports multiple shell programs. Each does the same core job but differs in features and syntax. | Shell | Name | Notes | |---|---|---| | `bash` | Bourne Again Shell | Most widely used. Default on most Linux distros and older macOS | | `zsh` | Z Shell | Bash-compatible with better autocomplete and plugins | | `sh` | Bourne Shell | Oldest, minimal features. Found on every Unix system | | `ksh` | Korn Shell | Common in enterprise Unix environments | | `fish` | Fish Shell | Beginner-friendly, syntax highlighting built in | To check which shell you are currently using: ```bash echo $SHELL # shows default shell (e.g. /bin/bash) echo $0 # shows current running shell cat /etc/shells # lists all shells installed on the system ``` ### What is Shell Scripting A shell script is a plain text file that contains a sequence of shell commands. Instead of typing commands one by one every time, you put them all in a file and run the file. The shell reads and executes them in order. Shell scripting is used for: * Automating repetitive tasks (backups, cleanup, deployments) * System monitoring and health checks * Processing files and logs * CI/CD pipeline steps * Managing cloud infrastructure via CLI ---
### Creating the File A shell script is just a text file. You can create it with any text editor. The convention is to use `.sh` as the file extension — it does not make it executable automatically, but it tells anyone reading the directory what the file is. ```bash nano myscript.sh # create and open in nano editor ``` ### The Shebang Line The very first line of every shell script should be the **shebang** (also called hashbang). It tells the operating system which interpreter should run this file. ```bash #!/bin/bash ``` The `#!` is the shebang. `/bin/bash` is the path to the bash interpreter. Without this line, the system may use the wrong shell or fail to run the script. ```bash #!/bin/bash # use bash (most common) #!/bin/sh # use sh (for portability across systems) #!/usr/bin/env bash # find bash wherever it is installed (recommended for portability) ``` ### Your First Script ```bash #!/bin/bash # This is a comment — ignored during execution # Comments help explain what the script does echo "Hello, World!" echo "Today's date is: $(date)" echo "Current user: $USER" echo "Current directory: $PWD" ``` `echo` prints text to the terminal. `$(date)` runs the `date` command and inserts its output. `$USER` and `$PWD` are environment variables that the shell provides automatically. ### Making the Script Executable When you create a new file, it has no execute permission by default. If you try to run it directly, you will get a "Permission denied" error. ```bash chmod +x myscript.sh # give execute permission # Now you can run it ./myscript.sh # ./ means "in the current directory" ``` ### Three Ways to Run a Script ```bash ./myscript.sh # run directly (requires execute permission) bash myscript.sh # run with bash explicitly (no chmod needed) source myscript.sh # run in current shell (variables persist after) ``` The difference with `source`: when you run `./myscript.sh` or `bash myscript.sh`, the script runs in a new child process — variables set inside it disappear when it finishes. When you use `source`, it runs in your current shell session — variables and changes persist. ### Comments ```bash #!/bin/bash # This is a single-line comment echo "This line runs" # You can comment out a command to disable it temporarily # echo "This line is disabled" : ' This is a multi-line comment block. Everything between the quotes is ignored. Useful for longer explanations. ' echo "Done" ``` ### Essential Configuration Files When you open a terminal, your shell reads configuration files to set up your environment — variables, aliases, functions, prompt appearance. Knowing which file does what saves a lot of debugging time. | File | When it runs | Use for | |---|---|---| | `~/.bashrc` | Every new interactive bash shell | Aliases, functions, prompt customization | | `~/.bash_profile` | Login shells only (SSH, terminal login) | Environment variables like PATH | | `~/.profile` | Login shells (sh-compatible) | Same as bash_profile but for sh | | `/etc/profile` | All users, login shells | System-wide settings | | `/etc/bashrc` | All users, all bash shells | System-wide aliases and functions | For most daily use, you put your custom settings in `~/.bashrc`: ```bash # Add to ~/.bashrc # Custom alias alias ll='ls -la' alias k='kubectl' alias gs='git status' # Add a directory to PATH export PATH="$HOME/.local/bin:$PATH" # Set default editor export EDITOR="nano" ``` After editing `~/.bashrc`, apply changes without reopening the terminal: ```bash source ~/.bashrc ``` ---
### Declaring Variables Variables in bash store values you want to reuse. You assign them with `=` and access them with `$`. ```bash #!/bin/bash name="Daksh" age=25 city="Mumbai" echo "Name: $name" echo "Age: $age" echo "City: $city" ``` Important rules for variable names: * No spaces around the `=` sign — `name = "Daksh"` is wrong, `name="Daksh"` is correct * Variable names are case-sensitive — `Name` and `name` are different variables * Names can contain letters, numbers, and underscores * Names cannot start with a number * Use lowercase for your own variables to avoid clashing with system variables which are usually uppercase ### Reading User Input The `read` command waits for the user to type something and stores it in a variable. ```bash #!/bin/bash echo "Enter your name:" read username echo "Hello, $username!" # Read with a prompt on the same line read -p "Enter your age: " age echo "You are $age years old" # Read a password (input hidden, not shown on screen) read -sp "Enter your password: " password echo "" # newline after hidden input echo "Password stored (not shown)" # Read with a timeout — continue after 5 seconds if no input read -t 5 -p "Quick! Enter something (5 seconds): " answer ``` ### Types of Variables **Local variables** — exist only in the current script or function ```bash #!/bin/bash myvar="hello" echo $myvar # works fine here ``` **Environment variables** — available to the current shell and all child processes. You export them. ```bash export DB_HOST="localhost" export DB_PORT="5432" # Now any command or script you run from here can see these ./myapp.sh # myapp.sh can access DB_HOST and DB_PORT ``` **Shell variables** — predefined by the shell itself. Always available. ```bash echo $HOME # home directory of current user echo $USER # current logged in username echo $PATH # directories to search for commands echo $PWD # current working directory echo $SHELL # current shell echo $HOSTNAME # machine hostname echo $BASH_VERSION # bash version ``` **Constant variables** — variables whose value should not change. Use `readonly`. ```bash readonly MAX_RETRIES=3 readonly APP_NAME="my-app" MAX_RETRIES=5 # this will fail — cannot change a readonly variable ``` ### Command Substitution You can store the output of a command in a variable. ```bash #!/bin/bash # Two equivalent syntaxes — $() is preferred current_date=$(date) current_date_old=`date` # older backtick syntax, avoid this files=$(ls /etc) hostname=$(hostname) free_memory=$(free -m | awk 'NR==2{print $4}') echo "Date: $current_date" echo "Hostname: $hostname" echo "Free memory: ${free_memory}MB" ``` ### Variable Scope — Local vs Global in Functions In bash, variables declared anywhere in a script (even inside a function) are **global by default**. To make a variable local to a function, use the `local` keyword. ```bash #!/bin/bash name="global name" # global variable myfunction() { local name="local name" # only exists inside this function echo "Inside function: $name" } myfunction echo "Outside function: $name" # still "global name" ``` Output: ``` Inside function: local name Outside function: global name ``` Without `local`, any variable changed inside a function changes the global version — which causes hard-to-find bugs in longer scripts. ---
### Arithmetic Operations Bash treats everything as a string by default. To do math, you need to tell it explicitly that you want arithmetic. ```bash #!/bin/bash a=10 b=3 # Method 1: $(( )) — recommended echo $((a + b)) # 13 echo $((a - b)) # 7 echo $((a * b)) # 30 echo $((a / b)) # 3 (integer division — no decimals) echo $((a % b)) # 1 (remainder/modulo) echo $((a ** b)) # 1000 (exponentiation) # Store result in variable result=$((a * b)) echo "Result: $result" # Method 2: expr (older, still works) result=$(expr $a + $b) echo $result ``` #### Increment and Decrement ```bash count=0 count=$((count + 1)) # increment ((count++)) # shorthand increment ((count--)) # shorthand decrement ((count += 5)) # add 5 echo $count ``` #### Floating Point — Use bc Bash cannot do decimal math natively. Use `bc` (basic calculator): ```bash result=$(echo "scale=2; 10 / 3" | bc) echo $result # 3.33 pi=$(echo "scale=5; 22 / 7" | bc) echo $pi # 3.14285 ``` `scale=2` sets 2 decimal places. ### Comparison Operators Used inside `if` statements to compare values. #### Numeric Comparisons | Operator | Meaning | Example | |---|---|---| | `-eq` | Equal to | `[ $a -eq $b ]` | | `-ne` | Not equal to | `[ $a -ne $b ]` | | `-gt` | Greater than | `[ $a -gt $b ]` | | `-ge` | Greater than or equal | `[ $a -ge $b ]` | | `-lt` | Less than | `[ $a -lt $b ]` | | `-le` | Less than or equal | `[ $a -le $b ]` | #### String Comparisons | Operator | Meaning | Example | |---|---|---| | `==` | Equal | `[ "$a" == "$b" ]` | | `!=` | Not equal | `[ "$a" != "$b" ]` | | `-z` | Empty string | `[ -z "$a" ]` | | `-n` | Not empty string | `[ -n "$a" ]` | #### File Test Operators These are very commonly used in DevOps scripts: | Operator | Meaning | |---|---| | `-f file` | File exists and is a regular file | | `-d dir` | Directory exists | | `-e path` | File or directory exists | | `-r file` | File is readable | | `-w file` | File is writable | | `-x file` | File is executable | | `-s file` | File exists and is not empty | ```bash if [ -f "/etc/nginx/nginx.conf" ]; then echo "Nginx config exists" fi if [ -d "/var/log" ]; then echo "Log directory exists" fi ``` ### Logical Operators ```bash # AND — both conditions must be true if [ $age -ge 18 ] && [ $age -le 60 ]; then echo "Working age" fi # OR — at least one condition must be true if [ $status -eq 0 ] || [ $status -eq 1 ]; then echo "Status is acceptable" fi # NOT — reverse the condition if ! [ -f "/tmp/lockfile" ]; then echo "No lockfile found, safe to proceed" fi ``` ---
### If-Else The most fundamental decision-making structure. The script runs one block of code if a condition is true, and a different block if it is false. ```bash #!/bin/bash read -p "Enter a number: " num if [ $num -gt 0 ]; then echo "$num is positive" elif [ $num -lt 0 ]; then echo "$num is negative" else echo "The number is zero" fi ``` The structure: * `if` starts the condition * `then` runs if the condition is true * `elif` adds another condition to check if the first was false * `else` runs if none of the conditions were true * `fi` closes the if block (it is `if` backwards) #### Real DevOps Example — Check if a Service is Running ```bash #!/bin/bash SERVICE="nginx" if systemctl is-active --quiet $SERVICE; then echo "$SERVICE is running" else echo "$SERVICE is not running — attempting to start" systemctl start $SERVICE if systemctl is-active --quiet $SERVICE; then echo "$SERVICE started successfully" else echo "Failed to start $SERVICE — check logs" exit 1 fi fi ``` #### Checking Exit Status Every command in Linux returns an exit code when it finishes. `0` means success. Any non-zero value means failure. ```bash #!/bin/bash ping -c 1 google.com > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "Internet connection is working" else echo "No internet connection" fi # $? holds the exit code of the last command ``` ### Case Statement When you have many possible values for one variable, `case` is cleaner than a chain of `elif` statements. ```bash #!/bin/bash read -p "Enter day (1-7): " day case $day in 1) echo "Monday" ;; 2) echo "Tuesday" ;; 3) echo "Wednesday" ;; 4) echo "Thursday" ;; 5) echo "Friday" ;; 6) echo "Saturday" ;; 7) echo "Sunday" ;; *) echo "Invalid day — enter 1 to 7" ;; esac ``` `*)` is the default case — it matches anything not matched above. `esac` closes the case block (case backwards). #### Case with Patterns ```bash #!/bin/bash read -p "Enter environment (dev/staging/prod): " env case $env in dev|development) echo "Deploying to development server" SERVER="dev.company.com" ;; staging|stage) echo "Deploying to staging server" SERVER="staging.company.com" ;; prod|production) echo "Deploying to PRODUCTION" SERVER="prod.company.com" ;; *) echo "Unknown environment: $env" exit 1 ;; esac echo "Target server: $SERVER" ``` ---
Loops let you repeat a block of commands multiple times — either a fixed number of times, or until a condition changes. ### For Loop Iterates over a list of items. ```bash #!/bin/bash # Loop over a list of values for color in red green blue yellow; do echo "Color: $color" done # Loop over a range of numbers for i in {1..5}; do echo "Count: $i" done # Loop with step — {start..end..step} for i in {0..20..5}; do echo $i # 0 5 10 15 20 done # C-style for loop (like most programming languages) for ((i=1; i<=5; i++)); do echo "Iteration: $i" done ``` #### Loop Over Files ```bash #!/bin/bash # Process every .log file in /var/log for logfile in /var/log/*.log; do echo "Processing: $logfile" wc -l "$logfile" # count lines in each file done # Loop over output of a command for user in $(cat /etc/passwd | cut -d: -f1); do echo "User: $user" done ``` #### For Loop to Read Values from a File ```bash #!/bin/bash # servers.txt contains one server hostname per line while IFS= read -r server; do echo "Checking $server..." ping -c 1 "$server" > /dev/null 2>&1 if [ $? -eq 0 ]; then echo "$server is reachable" else echo "$server is DOWN" fi done < servers.txt ``` ### While Loop Runs as long as a condition is true. Use when you do not know in advance how many times to loop. ```bash #!/bin/bash count=1 while [ $count -le 5 ]; do echo "Count is: $count" ((count++)) done ``` #### While Loop Reading a File Line by Line ```bash #!/bin/bash while IFS= read -r line; do echo "Line: $line" done < /etc/hosts ``` `IFS=` prevents leading/trailing whitespace from being stripped. `-r` prevents backslash processing. This is the correct and safe way to read files line by line. #### While Loop for Retry Logic ```bash #!/bin/bash MAX_RETRIES=5 attempt=1 while [ $attempt -le $MAX_RETRIES ]; do echo "Attempt $attempt of $MAX_RETRIES..." curl -s https://api.example.com/health > /dev/null if [ $? -eq 0 ]; then echo "Service is up!" break # exit the loop fi echo "Service not ready, waiting 10 seconds..." sleep 10 ((attempt++)) done if [ $attempt -gt $MAX_RETRIES ]; then echo "Service failed to respond after $MAX_RETRIES attempts" exit 1 fi ``` ### Until Loop Opposite of while — runs until a condition becomes true (runs while it is false). ```bash #!/bin/bash count=1 until [ $count -gt 5 ]; do echo "Count: $count" ((count++)) done ``` Not used as often as while, but useful when the logic reads more naturally as "until X happens, keep doing Y." ### Infinite Loop Sometimes you genuinely want something to run forever — like a monitoring daemon. ```bash #!/bin/bash while true; do echo "Checking disk usage..." df -h | grep -E '([89][0-9]|100)%' # alert if any disk is over 80% full sleep 60 # check every 60 seconds done ``` ### Break and Continue `break` exits the loop immediately. `continue` skips the rest of the current iteration and jumps to the next one. ```bash #!/bin/bash for i in {1..10}; do if [ $i -eq 3 ]; then continue # skip 3 fi if [ $i -eq 7 ]; then break # stop at 7 fi echo $i done # Output: 1 2 4 5 6 ``` ---
When you open a terminal on a Linux machine and type a command, something has to read what you typed, understand it, and...
Creating the File A shell script is just a text file. You can create it with any text editor. The convention is to use ....
Declaring Variables Variables in bash store values you want to reuse. You assign them with = and access them with $. Imp...
Arithmetic Operations Bash treats everything as a string by default. To do math, you need to tell it explicitly that you...
If-Else The most fundamental decision-making structure. The script runs one block of code if a condition is true, and a ...
Loops let you repeat a block of commands multiple times — either a fixed number of times, or until a condition changes. ...
Writing Functions A function is a named block of reusable commands. Instead of writing the same logic in multiple places...
Arrays let you store multiple values in a single variable — useful for managing lists of servers, files, environments, o...
Input and Output Redirection By default, commands read from the keyboard (standard input) and write to the screen (stand...
These three tools are the most powerful text processing utilities in Linux. Every DevOps engineer uses them daily — for ...
Checking Running Processes Killing Processes Background Jobs nohup prevents the process from stopping when you close the...
What is Cron Cron is the Linux job scheduler. It runs commands or scripts automatically at specified times — every minut...
Logging in Scripts Good scripts write logs. This makes it easy to know what happened when a script ran at 3 AM. tee -a w...
curl is a command-line tool for making HTTP requests. In shell scripts, it is used to call REST APIs, check health endpo...
These commands are used in scripts that monitor disk usage, clean up old files, and manage storage. df — Disk Free Space...
Project 1 — Monitor Free RAM and Alert Project 2 — Disk Space Monitor with Email Alert Project 3 — Archive Older Log Fil...
...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.