Skip to content

sahilrajput03/learning-bash

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Readme

Quick Links

Running these scripts

  • Use Code Runner (VsCode Extension) - Google Doc
    • ❤️ ctrl+option+n to run the currently focussed file.
  • Using bmon from the my_bin repository (or using bm)

Format .bash and .sh file on save in VsCode

Just install this extension: https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format and turn the default format in vsocde setting as 'null', so that appropriate formatters will be chosen for appropriate files.

Code Generated via AutoDocs

File - 0.sh

Simple echo example.

#!/usr/bin/env bash -x
# We enable debug mode with above -x above. Alternatively, we can run
#   `set -x` explicitly to enable it and `set +x` to disable it.

echo Hello, from example.
# Output:
#   + echo Hello, from example.
#   Hello, from example.

# 🚀 My recent bash scripts:
# qr-solution-backend/scripts/consistent-dev.mypot.in.sh
# qr-solution-backend/scripts/work-track-1-min-poll.sh
# qr-solution-backend/scripts/merge-develop-into-main-branch-and-push-both.sh

File - 1.sh

#!/usr/bin/env bash

if [ "$SHELL" = "/bin/zsh" ]; then
    echo "âś…Shell is /bin/zsh"
else
    echo "❤️SHELL is $SHELL"
fi
# Output (in zsh shell):
#    âś…Shell is /bin/zsh

# Process id of this script:
echo "PID: $" # double $ sign [autodocs issue]
# Output:
# PID: 8264

File - 2.sh

#!/usr/bin/env bash
var=~/Desktop
cd $var
ls

File - 3.sh

#/usr/bin/env bash
# Learn:
# 1. If you run the script via 'sudo' prefixed you would see `0` value
#       because that denotes if the program is run with root access
#       or not.
# 2. If the user executing the script has root permissions by default
#       then wihout using `sudo` the output would be `0`.
echo $UID

File - 4.sh

#!/usr/bin/env bash

# Learn you can use either `UID` or `EUID`, both performs exactly same in
#     my testing experience. EUID is almost always used by the kernel and
#     most system calls (e.g., open(), chmod(), kill()) to check
#     permissions.

# * Checking if root user at top level (not inside any function)

echo "UID: $UID"
# Output: 0 (Always 0 for "root" user)
# Output: 1000 (for "array" user)

require_root() {
  ROOT_UID=0 # Only users with $UID 0 have root privileges.
  if [ "$UID" -ne "$ROOT_UID" ]; then
    echo "Must be root to run this script."
    return 1
  fi
}

require_root || exit 1
echo Congrats you are root user.

File - 5.sh

#!/usr/bin/env bash
ROOT_UID=0
if [ "$UID" = "$ROOT_UID" ]; then
  echo "Yes, you have root permissions!"
fi

if [ "$UID" != "$ROOT_UID" ]; then
  echo "No, you dont have root permissions!"
fi

if [ "$UID" -ne "$ROOT_UID" ]; then # -ne is same as !=
  echo "No, you dont have root permissions! (WITH '-ne')"
fi

if [ "$UID" = 1000 ]; then # POSIX sh
  echo "You are a simple user with UID as '1000'."
fi

if [ "$UID" == 1000 ]; then # bash specific
  echo "You are a simple user with UID as '1000'."
fi

# A phenomenal usage:
# https://stackoverflow.com/a/40431336/10012446

File - 6.sh

#!/usr/bin/env bash

# There is lots of way to compare value like string and numbers
#       differenctly and using the C way i.e., == operator as well.
#       Read this amazing stackoverflow answer to learn about it:
#       https://stackoverflow.com/a/40431336/10012446

File - 7.sh

#!/usr/bin/env bash
#This file demonstrates simple string manipulation with variables.
echo -----
A="Sahil"       #Note you should ALWAYS use quotes around text when assigning it to variable.
B="${A} Rajput" #This is how we concatenate text to string in bash.

echo $A
echo $B

File - 8.sh

#!/usr/bin/env bash
# * Learn about `return`

a() {
    echo "🚀Running function a"
}
a
exitCode=$?
echo -e "returnValue/exitCode of function a: $exitCode \n"
# OUTPUT: returnValue/exitCode code of function a: 0

a() {
    echo "🚀Running function a"
    return
}
a
exitCode=$?
echo -e "returnValue/exitCode of function a: $exitCode \n"
# OUTPUT: returnValue/exitCode of function a: 0

File - 9.sh

#!/usr/bin/env bash
# * Learn about `return` and `exit` keywords

a() {
    echo "🚀Running function a"
    return 1 # Default returnValue is 0
}
a
exitCode=$?
echo -e "returnValue/exitCode of function a: $exitCode \n"
# OUTPUT: returnValue/exitCode of function a: 1

b() {
    echo "🚀Running function b"
    # Learn: Here command a fails thus return expression is executed
    #       and then the returnCode of function a is returned from
    #       function b.
    a || return
    echo "🎉 Function b ends here." # ❌This line is never run.
}
b
exitCode=$?
echo -e "returnValue/exitCode of function b: $exitCode \n"
# OUTPUT: returnValue/exitCode of function b: 1

c() {
    echo "🚀Running function c"
    a || return 2
    echo "🎉 Function c ends here." # ❌This line is never run.
}
c
exitCode=$?
echo -e "returnValue/exitCode of function c: $exitCode \n"
# OUTPUT: returnValue/exitCode of function c: 2

# âś…Learn:
#       1. At top level we can only use `exit` because `return` is not
#           valid at top level (i.e, we get error if we use `return`
#           at top level)
#       2. If we use `exit` instead of `exit 3` below then exitCode
#           of this script will be 1 because function a returns 1
a || exit 3
echo Program never reaches hereâś… # This line is never run.

File - 10.sh

#!/usr/bin/env bash

# -e – file exists (whether regular file, directory, etc.)
# -f – file exists and is a regular file
# -d – file exists and is a directory

if [ -e "10.sh" ]; then
    echo "âś…File exists."
else
    echo "❌File does not exist."
fi
# Output: âś…File exists.

if [ -f "10.sh" ]; then
    echo "âś…File exists."
else
    echo "❌File does not exist."
fi
# Output: âś…File exists.

if [ -d ".github" ]; then
    echo "âś…Directory exists."
else
    echo "❌Directory does not exist."
fi
# Output: âś…Directory exists.

File - 11.1.0.sh

#!/usr/bin/env bash

# 🚀 Send system notifications in macos

# 🚀 You can run a process in background in macos using `air` function
#       from the file ~/.zshrc (Tags: #true background process,
#           #detach process). [âś…TESTED]
#   air -l 11.1.0.log bash 11.1.0.sh
echo "PID: $" # double $ sign [autodocs issue]

# Also:
#  âś…To view process id do    `pgrep -f 11.1.0.sh`
#  âś…To kill background process then you can use      ```pkill -f 11.1.0.sh```

counter=0
while true; do
    counter=$((counter + 1)) # increment by 1
    # We output counts to stdout to test viewing logs of this process anytime in future:
    echo $counter

    # We send system notifications so we can know that process is runnning anytime in future:
    osascript -e "display notification \"$counter\" with title \"Test\""
    sleep 2
done

File - 11.1.sh

#!/usr/bin/env bash -x

# 🚀 Run a command on program exit
# 🚀 Learn `wait`, `sleep` command.

# âś… Learn: You can use microseconds with sleep command too:
# sleep 0.1

# 1. Amazing guide to using trap by linuxjournal.com at https://www.linuxjournal.com/content/bash-trap-command
# 2. Set a function to be executed in the trap commands. Source: https://stackoverflow.com/a/3338302/10012446
# 3. Reset trap command:
#   Source: https://stackoverflow.com/a/31202968/10012446
#   Source2: https://bash.cyberciti.biz/guide/How_to_clear_trap#:~:text=Enter%20number%20(%2D9999%20to%20exit)%20%3A%20999%20999%20is%20an,exit)%20%3A%20%2D9999%20Bye!

# Create file abc.txt
touch abc.txt

# Learn: `trap` runs command if:
#   1. program exit natually on its own.
#   2. ctrl+c i.e., [SIGINT (2)]
COMMAND="rm abc.txt"
trap "$COMMAND" exit

# Create a background running program --- check it via `pgrep sleep`
sleep 100 &

# âś… `wait` command
# 1. The wait command in Bash pauses the execution of a script until
#      all background jobs or a specific job (by PID or job ID)
#      finish.
# 2. It returns the exit status of the waited-for process.
# 3. Usage:
#       wait → waits for all background jobs to complete.
#       wait <PID> → waits for a specific process ID to complete.
#       wait %<job_id> → waits for a specific job (e.g., %1) to complete.
# 4. Keep the script running until previous background processes
#    before the wait command are running so you can intercept ctrl+c
#    signal to kill this script and trigger trap command to kill the
#    earlier background processes as well.
wait

File - 11.2.sh

#!/usr/bin/env bash -x

# 🚀 Run a bash function on program exit

bye() {
    echo "Bye bye"
}

# Runs function if:
#   1. program exit natually on its own.
#   2. ctrl+c i.e., [SIGINT (2)]
trap bye exit

echo "Hello world"
# sleep 100

wait

File - 11.3.sh

#!/usr/bin/env bash -x
# we enable debug mode with above -x

# 🚀 Kill all background jobs using `jobs -p` on exit

# create two processes for 99 seconds in background and kill them on exit with trap
sleep 99 &
sleep 99 &

# âś… Kill all background jobs on exit via `jobs -p` [TESTED]
bgProcessIds="$(jobs -p | xargs)"
trap "kill $bgProcessIds; echo 'âś…Killed background processes: $bgProcessIds'" EXIT

# In any temrinal you can check if the process is running with:
# ps aux | grep '[s]leep 1000'

wait

File - 11.4.sh

#!/usr/bin/env bash -x

# 🚀 Kill individual processes on exit

# Create two processes with sleep 5 in background and kill them on
#   exit with trap
sleep 1000 &
process1=$!

sleep 1000 &
process2=$!

trap "kill $process1 $process2" EXIT

# In any temrinal you can check if the process is running with:
# ps aux | grep '[s]leep 1000'

wait

File - 11.5.sh

#!/usr/bin/env bash -x

# 🚀 Kill process using `pkill` to match process name on exit

trap 'pkill -f tailwind' exit

# Run tailwindcss with watch mode and send it to background
tailwindcss -w -i ./app/static/src/main.css -o ./app/static/dist/main.css --minify &

flask run

# Another intersting usage found in cloud-init docs to stop python server on exiting qemu instance shell command:
# Check @ https://github.com/sahilrajput03/config/blob/852bda337a6de7df6e767b01bffa3ee9835e9914/_linode/testing-cloud-init-with-qemu/with-archlinux-cloudimg/SCRIPT_QEMU.sh

File - 11.6.sh

#!/usr/bin/env bash

# 🚀 Running two while loops in background and killing them `ctrl+c`
# 🚀 Learn: `pgrep` (pgrep -f), `pkill` (pkill -f), `osascript` Cli
#         to show system notifications in macos.

echo "PID of the current shell: $" # double $ sign [autodocs issue]

# First task in asynchronous mode
while true; do
    osascript -e 'display notification "Message 1" with title "Title 1"'
    sleep 2
done &
PID1=$!
echo "PID1: $PID1"

# Second task (another loop or command) in asynchronous mode
while true; do
    sleep 1 # offsync this loop by 1 second
    osascript -e 'display notification "Message 2" with title "Title 2"'
    sleep 2
done &
PID2=$!
echo "PID2: $PID2"

# Print all background jobs:
echo "Background jobs process ids via 'jobs -p':"
jobs -p

# This is executed when you press ctrl+c
trap "kill $PID1 $PID2; echo 'âś…Killed background processes: $PID1 $PID2'" EXIT

# âś… Kill all background jobs on exit via `jobs -p` [TESTED]
# bgProcessIds="$(jobs -p | xargs)"
# trap "kill $bgProcessIds; echo 'âś…Killed background processes: $bgProcessIds'" EXIT

wait

# * âś… For manual tracking if it gets orphaned in worst case:
# ps au | grep '[1]1.6'
# apple  5284   0.0  0.0 33742688    712 s024  S+    5:04PM   0:00.02 /bin/bash ./11.6.sh
# apple  5283   0.0  0.0 33743712    724 s024  S+    5:04PM   0:00.01 /bin/bash ./11.6.sh
# apple  5267   0.0  0.0 33728352   1016 s024  S+    5:04PM   0:00.01 /bin/bash ./11.6.sh

# * âś… Check process in background (all below works):
# pgrep -f 11.6.sh
# pgrep -f 11.6
# pgrep -f ./11.6
# pgrep -f 'bash ./11.6.sh'

# * âś… Kill process via:
# pkill -f 11.6

# & 🚀 What does -f do in `pgrep` and `pkill` command?
# Consider this process output from `ps au` command:
#       apple 46985   ...   /bin/bash ./11.6.sh
# Here:
#       COMMAND: sh
#       ARGUMENT: ./a.s
#       FULL COMMAND LINE: sh ./a.s
# * 🚀 So, using pgrep -f matches against this full line (sh ./a.s), rather
#   than just the command (sh), allowing for more precise filtering.

File - 12.sh

#!/usr/bin/env bash

# From ChatGPT: https://chatgpt.com/c/682a21f9-c860-8007-b816-51d90d5438ae

# VARIABLE MEANING
# $ 	    Process ID (PID) of the current shell.
#   ^ (double $ sign)[#autodocs issue]
# $?	    Exit status of the last executed command. 0 means success, any other value usually indicates an error.
# $!	    PID of the last background command.
# $0	    Name of the script itself.
# $1 to $9	Positional parameters: the first to ninth arguments passed to the script/function.
# $@	    All positional parameters, as separate words. Preserves quotes.
# $*	    All positional parameters as a single word (may not preserve quotes).
# "$@"	    Preferred when passing arguments to another command/function.
# "$*"	    Treats all arguments as a single string.

echo "Script name: $0"
echo "First arg: $1"
echo "All args: $@"
echo "PID of this script: $" # double $ sign [autodocs issue]

sleep 5 &
echo "Background process PID: $!"

false
echo "Exit status of 'false' command: $?"

File - 13.1.sh

#!/usr/bin/env bash

# 🚀 Run commands on failure of some commands via inline way

echo Good Command && echo Success✅ && echo Success2✅ || echo Failed❌
# Good Command
# Successâś…
# Success2âś…

echo

BadCommand && echo Success✅ || echo Failed❌ && echo Failed2❌
# Output:
# ./13.1.sh: line 10: BadCommand: command not found
# Failed❌
# Failed2❌

echo

# 🚀 Run multiple commands on failure of some commands via using braces way
TEMP_DIR=""
mkdir "$TEMP_DIR" && cd "$TEMP_DIR" || {
    echo "❌Error: Failed to create directory [$TEMP_DIR], aborting early"
    exit 1
}
# Output:
# mkdir: .: No such file or directory
# ❌Error: Failed to create directory [], aborting early

echo

badCmd1 && {
    badCmd2 && echo 'success' || echo 'badCmd2 failed'
} || echo 'cmd1 failed'
# Output:
#   ./13.1.sh: line 16: badCmd1: command not found
#   cmd1 failed

echo

echo "wow" && {
    badCmd2 && echo 'success' || echo 'badCmd2 failed'
} || echo 'cmd1 failed'
# Output:
#   wow
#   ./13.1.sh: line 21: badCmd2: command not found
#   badCmd2 failed

File - 14.sh

#!/usr/bin/env bash

# 🚀Create file myfile.txt with content using cat command

# Note: If file already exists, it will be overwritten [TESTED]
cat <<EOF >myfile.txt
Hello world
EOF

File - 15.sh

#!/usr/bin/env bash

# ❤️ Force a script to be run in current shell by throwing error if run without `source` or `.`
# * 🚀 Using `&` to run commands in background and switching to them using `fg`

# & ⚠️ NOTE: This script must be sourced (run with 'source' command or
# &          '.') to work properly [TESTED on linux and macos]
# Example: source 15.sh or . ./15.sh

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    echo "Please run this script using 'source' command or '.'"
    exit 1
fi

# Start a Python HTTP server (port 8000) for serving files in current directory
# This runs in background and can be brought to foreground with 'fg' command
python3 -m http.server --directory . &

echo "âś…Process started with process ID: $!"

# Also, you can:
#   - get process ID via `pgrep python`
#   - kill process via via `pkill python`

# Now you can switch back to this serve using fg command
# fg

File - 16.sh

#!/usr/bin/env bash

echo

# time sleep 1
# Output (MACOS):
# real    0m1.007s
# user    0m0.001s
# sys     0m0.002s

# time bash -c 'sleep 1; sleep 1'
# Output (MACOS):
# real    0m2.026s
# user    0m0.007s
# sys     0m0.010s

# Learn: Its necessary to use braces {} around so that we can pipe the
#        output of time to grep
echo -n "âś…real time: "
{ time sleep 1; } 2>&1 | grep "real" | awk '{print $2}'
# Output: âś…real time: 0m1.013s

echo -n "âś…real time: "
{ time bash -c 'sleep 1 && sleep 1'; } 2>&1 | grep "real" | awk '{print $2}'
# Output: âś…real time: 0m2.037s

calculate_time() {
    local cmd="$1"
    time_taken=$({ time bash -c "$cmd"; } 2>&1 | tee /dev/tty | grep "real" | awk '{print $2}')
    echo "âś…real time: $time_taken"
}

# Example usage
calculate_time 'sleep 1 && echo boom && sleep 1'
# Output:
# boom
#
# real    0m2.031s
# user    0m0.010s
# sys     0m0.013s
# âś…real time: 0m2.031s

File - 17.sh

#!/usr/bin/env bash

#  Array / List in bash and zsh (`sh` lacks array support)
array=(item1 item2 item3)

# "${FILES[@]}" safely expands to each element.
for item in "${array[@]}"; do
    echo "$item"
done
# Output:
# item1
# item2
# item3

echo -e "\nâś…All elements of array:"
echo ${array[@]}
# Output: item1 item2 item3

echo ${array[*]}
# Output: item1 item2 item3

# 🔍 [@] vs [*] — What's the Difference?
# ${array[@]}: Each element is treated as a separate word, preserving spaces.
# ${array[*]}: All elements are treated as one word, especially inside quotes.

File - 18.1.sh

#!/bin/bash

# 1. My own cronjob (inspiration: https://chatgpt.com/c/6841590c-8708-8007-a55e-81307fee6562)

while true; do
    current_time=$(date +%H:%M:%S)
    echo $current_time

    target_time="14:46:50"
    if [ "$current_time" == "$target_time" ]; then
        echo âś… The time is $target_time.
    fi
    sleep 1
done

File - 18.2.sh

#!/usr/bin/env bash

# 2. My own cronjob (inspiration: https://chatgpt.com/c/6841590c-8708-8007-a55e-81307fee6562)
# âś…Tested in Macos

# ⭕️ Note: In bash most probably the code from chatgpt
#       link above will work because I command from above didn't work in zsh
#       and I used copilot to fix it for zsh in macos)

targetTime="19:59"
target_epoch=$(date -j -f "%Y-%m-%d %H:%M" "$(date +%Y-%m-%d) $targetTime" +%s)
echo 🚀 ~ target_epoch: $target_epoch
current_epoch=$(date +%s)
echo 🚀 ~ current_epoch: $current_epoch
sleep_seconds=$((target_epoch - current_epoch))
echo 🚀 ~ sleep_seconds: $sleep_seconds

if [ $sleep_seconds -gt 0 ]; then
    sleep $sleep_seconds
else
    echo "❌Error: Sleep seconds must be greater than 0" >&2
    exit 1
fi

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages