Shell Interview Questions with Answers
Most Asked Shell Interview Questions for Software Engineer Roles
Introduction
This page provides a complete collection of Shell Interview Questions and Answers designed for frontend developers, full-stack developers, React developers, Angular developers, and software engineers preparing for technical interviews. Shell is a strongly typed programming language developed by Microsoft that extends JavaScript by adding static typing, interfaces, advanced type checking, and modern development features. It helps developers build scalable and maintainable applications. This interview guide covers beginner, intermediate, and advanced Shell concepts including types, interfaces, classes, generics, decorators, utility types, modules, Shell with React, Angular, Node.js, and real-world coding interview scenarios.
Why Shell?
- Strong static typing – catches errors at compile time, reducing runtime bugs
- Excellent tooling and IDE support – autocompletion, navigation, and refactoring
- Superset of JavaScript – works seamlessly with all existing JavaScript libraries
- Used by major frameworks like React, Angular, and Vue – essential for large-scale apps
- Enables scalable and maintainable enterprise-grade applications
- Growing community, continuous improvements, and high demand in the job market
Most Asked Shell Interview Questions
Shell scripting is the process of writing programs in a shell (command-line interpreter) to automate tasks, manage systems, and perform various operations.
- Automation: Automate repetitive tasks
- System administration: Manage and configure systems
- Batch processing: Process files and data
- Integration: Connect different tools
- Portability: Works across Unix-like systems
#!/bin/bash
# Hello World in Shell
echo "Hello, World!"Variables in shell are declared using the variable=value syntax. No spaces around the equals sign.
- Declaration:
name="value" - Access:
$nameor{name} - Local variables:
local var="value" - Environment variables:
export VAR="value" - Read-only:
readonly VAR="value"
#!/bin/bash
# Variables in Shell
x=10 # Integer
y=3.14 # Float (as string)
name="Shell" # String
is_active=true # Boolean (as string)
echo $x
echo $y
echo $name
echo $is_activeShell is dynamically typed. Everything is treated as a string, but arithmetic operations can be performed on numeric strings.
- Strings:
"Hello" - Integers:
10 - Arrays:
(1 2 3) - Associative arrays:
declare -A map - Booleans:
true,false
#!/bin/bash
# Data Types in Shell
# Shell is dynamically typed - everything is a string
# Integer
a=10
# Float (as string)
d=3.14
# String
f="Hello Shell"
# Boolean (as string)
g=true
h=false
# Array
j=(1 "hello" 3.14)
k=(1 2 3 4 5)
# Associative array (Bash 4+)
declare -A l
l[name]="Shell"
l[version]=5.0
# Null (unset variable)
m=""
echo $a
echo ${k[0]}Functions in shell are defined using the function_name() { commands; } syntax or function function_name { commands; }.
- Syntax:
my_function() { echo "Hello"; } - Parameters:
$1,$2 - Return:
return value - Local variables:
local var="value" - Export:
export -f function_name
#!/bin/bash
# Functions in Shell
# Function declaration
add() {
echo $(( $1 + $2 ))
}
# Function with default parameters
greet() {
local name="${1:-Guest}"
echo "Hello, $name!"
}
# Function with return value
subtract() {
echo $(( $1 - $2 ))
}
# Function with variable arguments
sum_all() {
local sum=0
for num in "$@"; do
sum=$((sum + num))
done
echo $sum
}
# Function with named arguments (using local variables)
create_person() {
local name="$1"
local age="${2:-0}"
local city="${3:-Unknown}"
echo "Name: $name, Age: $age, City: $city"
}
# Usage
add 5 3
greet "Alice"
subtract 10 4
sum_all 1 2 3 4 5
create_person "Alice" 25 "NYC"Arrays in shell are ordered collections that can hold multiple values. They support both indexed and associative arrays.
- Indexed arrays:
arr=(1 2 3) - Associative arrays:
declare -A arr - Access:
{arr[0]} - Add:
arr+=(4) - Length:
{#arr[@]}
#!/bin/bash
# Arrays in Shell
arr=(1 2 3 4 5)
# Map - transform each element
doubled=()
for i in "${arr[@]}"; do
doubled+=($((i * 2)))
done
echo "${doubled[@]}"
# Filter - select elements
evens=()
for i in "${arr[@]}"; do
if [ $((i % 2)) -eq 0 ]; then
evens+=($i)
fi
done
echo "${evens[@]}"
# Reduce - aggregate
sum=0
for i in "${arr[@]}"; do
sum=$((sum + i))
done
echo $sum
# Push and pop
arr+=(6)
echo "${arr[@]}"
unset 'arr[-1]'
echo "${arr[@]}"
# Array operations
a=(1 2 3)
b=(4 5 6)
c=()
for i in "${!a[@]}"; do
c+=($((a[i] + b[i])))
done
echo "${c[@]}"Associative arrays in shell are key-value pairs (dictionaries) declared with declare -A.
- Declaration:
declare -A map - Add:
map[key]="value" - Access:
{map[key]} - Keys:
{!map[@]} - Check:
[[ -v map[key] ]]
#!/bin/bash
# Associative Arrays (Dictionaries) in Shell
declare -A person
person[name]="Alice"
person[age]=25
person[city]="NYC"
# Access values
echo "${person[name]}"
echo "${person[age]}"
# Add/update values
person[country]="USA"
person[age]=26
# Get with default
city="${person[city]:-Unknown}"
# Keys and values
echo "${!person[@]}"
echo "${person[@]}"
# Iterate over associative array
for key in "${!person[@]}"; do
echo "$key => ${person[$key]}"
done
# Delete key
unset 'person[country]'
# Check if key exists
if [[ -v person[name] ]]; then
echo "Name exists"
fi
# Associative array from arrays
keys=(1 2 3 4 5)
values=(1 4 9 16 25)
declare -A squares
for i in "${!keys[@]}"; do
squares[${keys[i]}]=${values[i]}
doneShell doesn't have native tuples, but arrays can be used as tuple-like structures for ordered collections.
- Arrays as tuples:
(1 "hello" 3.14) - Access:
{tuple[0]} - Unpacking:
read a b c <<< "$tuple" - Return multiple values: Use
echowithread - Concatenation:
t3=("{t1[@]}" "{t2[@]}")
#!/bin/bash
# Arrays as Tuples in Shell
# Create tuple-like array
t=(1 "hello" 3.14 true)
# Access elements
echo "${t[0]}"
echo "${t[1]}"
# Array unpacking
tuple=(10 20 30)
a=${tuple[0]}
b=${tuple[1]}
c=${tuple[2]}
echo "$a, $b, $c"
# Function returning multiple values
divide() {
local a=$1
local b=$2
echo $((a / b))
echo $((a % b))
}
read quotient remainder < <(divide 10 3)
echo "Quotient: $quotient, Remainder: $remainder"
# Array concatenation
t1=(1 2 3)
t2=(4 5 6)
t3=("${t1[@]}" "${t2[@]}")
echo "${t3[@]}"Shell provides standard control flow statements including conditionals, loops, and case statements.
- If-else:
if [ condition ]; then ... fi - For loop:
for i in {1..10}; do ... done - While loop:
while [ condition ]; do ... done - Case:
case $var in pattern) ...;; esac - Break/Continue:
break,continue
#!/bin/bash
# Control Flow in Shell
# If-else statement
age=25
if [ $age -lt 18 ]; then
echo "Minor"
elif [ $age -lt 65 ]; then
echo "Adult"
else
echo "Senior"
fi
# Ternary-like (using && and ||)
[ $age -ge 18 ] && status="Adult" || status="Minor"
echo $status
# For loop
for i in {1..5}; do
echo $i
done
# For loop with array
fruits=("apple" "banana" "orange")
for fruit in "${fruits[@]}"; do
echo $fruit
done
# While loop
i=1
while [ $i -le 5 ]; do
echo $i
i=$((i + 1))
done
# Break and continue
for i in {1..10}; do
if [ $i -eq 6 ]; then
break
fi
if [ $((i % 2)) -eq 0 ]; then
continue
fi
echo $i
doneArrays in shell can be generated using loops, brace expansion, seq, and various built-in mechanisms.
- Brace expansion:
{1..10} - Seq:
$(seq 1 10) - Loops:
for i in {1..10}; do arr+=($i); done - Map: Transform with loops
- Filter: Conditional addition
#!/bin/bash
# Array Generation in Shell
# Using sequence
squares=()
for i in {1..10}; do
squares+=($((i * i)))
done
echo "${squares[@]}"
# Filter with condition
evens=()
for i in {1..20}; do
if [ $((i % 2)) -eq 0 ]; then
evens+=($i)
fi
done
echo "${evens[@]}"
# Nested loops
pairs=()
for i in {1..3}; do
for j in {1..3}; do
pairs+=("$i,$j")
done
done
echo "${pairs[@]}"
# Conditional array
results=()
for i in {1..10}; do
if [ $((i % 2)) -eq 0 ]; then
results+=("even")
else
results+=("odd")
fi
done
echo "${results[@]}"
# Using seq
for i in $(seq 1 10); do
echo $i
doneShell provides various string manipulation operations including concatenation, substitution, and extraction.
- Concatenation:
"$str1 $str2" - Length:
{#string} - Substring:
{string:0:5} - Replace:
{string/old/new} - Case conversion:
{string^^},{string,,}
#!/bin/bash
# Strings in Shell
# String creation
str1="Hello"
str2='World'
str3="Multi-line
string"
# String concatenation
greeting="$str1 $str2"
echo "$greeting"
# String interpolation
name="Shell"
version=5.0
echo "Welcome to $name version $version"
# String functions
text="Hello, World!"
echo "${#text}"
echo "${text^^}"
echo "${text,,}"
echo "${text/World/Shell}"
# Substring
echo "${text:0:5}"
# Split and join
IFS=' ' read -ra words <<< "Hello World Shell"
echo "${words[@]}"
joined=$(IFS='-'; echo "${words[*]}")
echo "$joined"
# String comparison
[ "hello" == "hello" ] && echo "Equal"
[ "hello" < "world" ] && echo "Less than"
# String formatting
printf "Value: %.2f
" 3.14159Shell modules are scripts that can be sourced (included) to reuse functions and variables across scripts.
- Sourcing:
source script.shor. script.sh - Functions: Define reusable functions
- Variables: Define constants
- Export:
export -f function_name - Libraries: Organize code into files
#!/bin/bash
# Functions and Scripts as Modules in Shell
# Creating a module (save as math.sh)
# add() { echo $(( $1 + $2 )); }
# subtract() { echo $(( $1 - $2 )); }
# Sourcing a script
# source math.sh
# . math.sh
# Using the module
# add 5 3
# subtract 10 4
# Creating a library
# save as lib.sh
# PI=3.14159
# add() { echo $(( $1 + $2 )); }
# Using library
# source lib.sh
# echo $PI
# Functions with local variables
my_function() {
local local_var="Local"
global_var="Global"
}
# Exporting functions
export -f my_function
# Including external files
# source ./config.sh
# Using alias as module
# alias my_alias='echo "Hello"'
# Creating a script module
# #!/bin/bash
# # mymodule.sh
# export MY_VAR="value"
# function my_func() { echo "Hello"; }
# Loading module
# source ./mymodule.sh
# my_funcShell variables are dynamically typed. The declare builtin can be used to set variable attributes.
- Local:
local var="value" - Global:
var="value" - Environment:
export var="value" - Read-only:
readonly var="value" - Special variables:
$0,$@,$#
#!/bin/bash
# Variables and Types in Shell
# Variable declaration
name="Alice"
age=25
city="NYC"
# Local variables
my_function() {
local local_var="Local"
global_var="Global"
}
# Environment variables
export ENV_VAR="Environment"
# Read-only variables
readonly CONSTANT=100
# Special variables
echo "Script name: $0"
echo "First argument: $1"
echo "All arguments: $@"
echo "Number of arguments: $#"
echo "Exit code of last command: $?"
echo "Process ID: $$"
# Variable types (using declare)
declare -i integer=42
declare -r readonly_var=100
declare -a array=(1 2 3)
declare -A associative=([key]="value")
# Checking if variable is set
if [ -z "${var+x}" ]; then
echo "var is unset"
fi
# Default value
name="${name:-Default}"
# Indirect expansion
var=name
echo ${!var}Shell provides type checking through pattern matching and built-in operators.
- Integer check:
[[ $var =~ ^-?[0-9]+$ ]] - Number check:
[[ $var =~ ^-?[0-9]*\.?[0-9]+$ ]] - Empty check:
[[ -z $var ]] - Set check:
[[ -v var ]] - Array check:
declare -p var
#!/bin/bash
# Type Checking in Shell
# Check if variable is integer
is_integer() {
[[ $1 =~ ^-?[0-9]+$ ]]
}
# Check if variable is number
is_number() {
[[ $1 =~ ^-?[0-9]*\.?[0-9]+$ ]]
}
# Check if variable is string (always true in shell)
is_string() {
[[ -n $1 ]]
}
# Check if variable is array
is_array() {
declare -p "$1" 2>/dev/null | grep -q 'declare -a'
}
# Check if variable is associative array
is_associative_array() {
declare -p "$1" 2>/dev/null | grep -q 'declare -A'
}
# Type checking examples
num=42
if is_integer "$num"; then
echo "$num is an integer"
fi
float=3.14
if is_number "$float"; then
echo "$float is a number"
fi
# Type conversion
number="42"
integer=$((number))
string="Hello"
# Checking variable existence
if [ -n "$var" ]; then
echo "var is set and not empty"
fi
# Type of variable
declare -p var 2>/dev/nullShell handles errors using exit codes, trap, and conditional execution.
- Exit codes:
exit 1 - Check:
if [ $? -ne 0 ]; then ... - trap:
trap 'command' ERR - set -e: Exit on error
- try-catch:
{ command } || { error_handler; }
#!/bin/bash
# Exception Handling in Shell
# Try-catch using trap
set -e
# Error handling with trap
error_handler() {
echo "Error occurred on line $1"
exit 1
}
trap 'error_handler $LINENO' ERR
# Try-catch pattern
{
# Code that might error
result=$((10 / 0))
echo $result
} || {
echo "Division by zero error"
}
# Specific error handling
{
arr=(1 2 3)
echo ${arr[10]}
} || {
echo "Index out of bounds"
}
# Finally block (using trap)
cleanup() {
echo "Cleanup performed"
}
trap cleanup EXIT
# Throwing errors
divide() {
if [ $2 -eq 0 ]; then
echo "Cannot divide by zero" >&2
return 1
fi
echo $(( $1 / $2 ))
}
# Using error
if ! result=$(divide 10 0); then
echo "Error: Division failed"
fi
# Custom error function
error() {
echo "ERROR: $1" >&2
exit 1
}
# Usage
# error "Custom error message"
# Checking command success
if command -v non_existent_command &> /dev/null; then
echo "Command exists"
else
echo "Command not found"
fiShell provides various commands for file operations including reading, writing, and manipulation.
- Read:
cat file - Write:
echo "text" > file - Append:
echo "text" >> file - Line by line:
while read -r line; do ... done < file - File info:
stat file,ls -la
#!/bin/bash
# File I/O in Shell
# Reading files
if [ -f "example.txt" ]; then
content=$(cat "example.txt")
echo "$content"
else
echo "File not found"
fi
# Reading line by line
if [ -f "data.txt" ]; then
while IFS= read -r line; do
echo "$line"
done < "data.txt"
fi
# Writing files
echo "Hello, World!" > output.txt
echo "This is line 2" >> output.txt
# Appending to files
echo "Appended line" >> output.txt
# Reading CSV
while IFS=',' read -r name age city; do
echo "Name: $name, Age: $age, City: $city"
done < data.csv
# Writing CSV
echo "Name,Age,City" > output.csv
echo "Alice,25,NYC" >> output.csv
echo "Bob,30,LA" >> output.csv
# File operations
for file in *.txt; do
echo "$file"
echo "$(stat -c%s "$file")"
done
# Reading with cat
cat file.txt
# Using heredoc
cat << EOF > output.txt
Line 1
Line 2
EOF
# Reading with read
read -p "Enter your name: " name
echo "Hello, $name"Shell uses system package managers like apt, yum, brew, and others for installing software packages.
- apt:
sudo apt install package - yum:
sudo yum install package - brew:
brew install package - Check:
dpkg -l | grep package - Environment:
export PATH=$PATH:/path
#!/bin/bash
# Packages and Package Managers in Shell
# Using apt (Debian/Ubuntu)
# sudo apt update
# sudo apt install package-name
# Using yum (RHEL/CentOS)
# sudo yum install package-name
# Using dnf (Fedora)
# sudo dnf install package-name
# Using brew (macOS)
# brew install package-name
# Using snap
# sudo snap install package-name
# Checking if package is installed
dpkg -l | grep -q package-name
if [ $? -eq 0 ]; then
echo "Package is installed"
fi
# Using pip for Python packages
# pip install package-name
# Using npm for Node.js packages
# npm install package-name
# Environment variables for packages
export PATH=$PATH:/usr/local/bin
# Loading package module
# module load package-name
# Creating a package script
# #!/bin/bash
# # mypackage.sh
# echo "My Package"
# Making script executable
# chmod +x myscript.sh
# Running script
# ./myscript.sh
# Installing from source
# ./configure
# make
# sudo make installShell can create plots using external tools like gnuplot, python matplotlib, R, or terminal-based utilities.
- gnuplot: Create plots from data
- python: Use matplotlib
- R: Use ggplot2
- asciiplot: Terminal plots
- spark: Terminal sparklines
#!/bin/bash
# Plotting in Shell
# Using gnuplot
gnuplot << EOF
set terminal png
set output "plot.png"
set title "Square Function"
set xlabel "x"
set ylabel "y"
plot [1:10] x**2 with lines
EOF
# Using python matplotlib from shell
python3 << EOF
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(1, 10, 0.1)
y = x**2
plt.plot(x, y)
plt.title("Square Function")
plt.xlabel("x")
plt.ylabel("y")
plt.savefig("plot.png")
EOF
# Using R from shell
Rscript -e '
x <- 1:10
y <- x^2
png("plot.png")
plot(x, y, type="l", main="Square Function")
dev.off()
'
# Creating data file for gnuplot
cat << EOF > data.dat
1 1
2 4
3 9
4 16
5 25
EOF
gnuplot << EOF
set terminal png
set output "plot2.png"
plot "data.dat" with linespoints
EOF
# Using asciiplot (terminal plots)
# npm install -g asciiplot
# asciiplot "1 4 9 16 25"
# Using spark (terminal sparklines)
# echo "1 2 3 4 5 6 7 8 9 10" | sparkShell provides arrays and associative arrays as primary data structures, with additional structures built using these.
- Stack: Array with push/pop
- Queue: Array with enqueue/dequeue
- Map: Associative arrays
- Set: Array with unique values
- Tree: Using filesystem or nested arrays
#!/bin/bash
# Data Structures in Shell
# Array as Stack (LIFO)
stack=()
push() {
stack+=("$1")
}
pop() {
local len=${#stack[@]}
if [ $len -gt 0 ]; then
local last=$((len - 1))
echo "${stack[$last]}"
unset 'stack[$last]'
stack=("${stack[@]}")
fi
}
# Array as Queue (FIFO)
queue=()
enqueue() {
queue+=("$1")
}
dequeue() {
if [ ${#queue[@]} -gt 0 ]; then
echo "${queue[0]}"
queue=("${queue[@]:1}")
fi
}
# Associative array as Map
declare -A map
map["key"]="value"
# Set using array
set_add() {
local element=$1
if [[ ! " ${set[@]} " =~ " $element " ]]; then
set+=("$element")
fi
}
# Usage
push 1
push 2
push 3
pop # returns 3
enqueue 1
enqueue 2
enqueue 3
dequeue # returns 1
# Environment as data structure
export MY_VAR="value"
# Using temporary files for data structures
temp_file=$(mktemp)
echo "data" > "$temp_file"
rm "$temp_file"Shell can perform statistical operations using bc, awk, sort, and custom functions.
- Mean: Sum / count
- Median: Sort and find middle
- Standard deviation: Custom calculation
- Correlation: Custom implementation
- Quantiles: Custom sorting
#!/bin/bash
# Statistics in Shell
# Basic statistics functions
mean() {
local sum=0
local count=0
for num in "$@"; do
sum=$((sum + num))
count=$((count + 1))
done
echo "scale=2; $sum / $count" | bc
}
median() {
local arr=("$@")
local n=${#arr[@]}
local sorted=($(printf '%s
' "${arr[@]}" | sort -n))
if [ $((n % 2)) -eq 1 ]; then
echo "${sorted[$((n/2))]}"
else
local left=${sorted[$((n/2 - 1))]}
local right=${sorted[$((n/2))]}
echo "scale=2; ($left + $right) / 2" | bc
fi
}
# Standard deviation
std_dev() {
local arr=("$@")
local avg=$(mean "${arr[@]}")
local sum=0
for num in "${arr[@]}"; do
local diff=$(echo "$num - $avg" | bc)
sum=$(echo "$sum + $diff * $diff" | bc)
done
local n=${#arr[@]}
echo "scale=2; sqrt($sum / $n)" | bc
}
# Correlation (simplified)
correlation() {
local x=("${!1}")
local y=("${!2}")
# Implementation would be more complex
}
# Usage
data=(1 2 3 4 5 6 7 8 9 10)
mean "${data[@]}"
median "${data[@]}"
std_dev "${data[@]}"Shell can perform linear algebra operations using arrays and external tools like bc or python.
- Matrix operations: Using arrays
- Transpose: Swap indices
- Multiplication: Dot product
- Norm: Sum of squares
- Vector ops: Dot product, norm
#!/bin/bash
# Linear Algebra in Shell
# Matrix operations
declare -A matrix
# Matrix multiplication
mat_mul() {
local -n A=$1
local -n B=$2
local -n R=$3
local rows=${#A[@]}
local cols=${#B[0,@]}
local inner=${#B[@]}
for ((i=0; i<rows; i++)); do
for ((j=0; j<cols; j++)); do
sum=0
for ((k=0; k<inner; k++)); do
sum=$((sum + A[$i,$k] * B[$k,$j]))
done
R[$i,$j]=$sum
done
done
}
# Transpose
transpose() {
local -n matrix=$1
local -n result=$2
local rows=${#matrix[@]}
local cols=${#matrix[0,@]}
for ((i=0; i<rows; i++)); do
for ((j=0; j<cols; j++)); do
result[$j,$i]=${matrix[$i,$j]}
done
done
}
# Vector operations
dot_product() {
local -n a=$1
local -n b=$2
local sum=0
for i in "${!a[@]}"; do
sum=$((sum + a[i] * b[i]))
done
echo $sum
}
# Norm
norm() {
local -n vector=$1
local sum=0
for val in "${vector[@]}"; do
sum=$((sum + val * val))
done
echo "scale=2; sqrt($sum)" | bc
}
# Usage
# mat_mul A B resultShell uses the date command for date manipulation, with formatting and arithmetic capabilities.
- Current:
date - Create:
date -d "2024-01-01" - Arithmetic:
date -d "$date + 10 days" - Difference:
$(( (date2 - date1) / 86400 )) - Formatting:
date +%Y-%m-%d
#!/bin/bash
# Dates and Time in Shell
# Current date and time
now=$(date)
echo "$now"
# Date creation
date1="2024-01-01"
date2="2024-01-01 12:00:00"
echo "$date1"
echo "$date2"
# Date arithmetic
date1=$(date -d "$date1 + 10 days" +%Y-%m-%d)
echo "$date1"
date1=$(date -d "$date1 + 2 months" +%Y-%m-%d)
echo "$date1"
# Date difference
diff=$(( ($(date -d "$date2" +%s) - $(date -d "$date1" +%s)) / 86400 ))
echo "$diff days"
# Formatting dates
date=$(date -d "2024-01-01" +%Y-%m-%d)
echo "$date"
# Date functions
echo $(date +%Y)
echo $(date +%m)
echo $(date +%d)
echo $(date +%A)
# Date range
start="2024-01-01"
end="2024-01-10"
current="$start"
while [[ $(date -d "$current" +%s) -le $(date -d "$end" +%s) ]]; do
echo "$current"
current=$(date -d "$current + 1 day" +%Y-%m-%d)
done
# Timestamps
timestamp=$(date +%s)
echo "$timestamp"
date=$(date -d "@$timestamp")
echo "$date"
# Timezone handling
TZ="America/New_York" dateShell supports regex through tools like grep, sed, awk, and Bash's =~ operator.
- grep:
grep "pattern" file - sed:
sed 's/pattern/replacement/g' - awk:
awk '/pattern/ {print}' - Bash:
[[ $text =~ pattern ]] - Capture groups:
{BASH_REMATCH[1]}
#!/bin/bash
# Regular Expressions in Shell
# Match
text="hello world"
if [[ $text =~ hello ]]; then
echo "Match found"
fi
# Find all (using grep)
text2="hello world hello again"
echo "$text2" | grep -o "hello"
# Regex with capture groups (using sed)
text3="Date: 2024-01-01"
if [[ $text3 =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
echo "Year: ${BASH_REMATCH[1]}"
echo "Month: ${BASH_REMATCH[2]}"
echo "Day: ${BASH_REMATCH[3]}"
fi
# Replace with regex (using sed)
replaced=$(echo "Hello 123 World" | sed 's/[0-9]+/NUM/g')
echo "$replaced"
# Case insensitive (using grep)
echo "$text3" | grep -i "hello"
# Split with regex (using awk)
parts=($(echo "Hello World Shell" | awk '{split($0,a,"[ ,]+"); print a[1], a[2], a[3]}'))
echo "${parts[@]}"
# Using grep
if echo "abc123def" | grep -q "[0-9]"; then
echo "Contains numbers"
fi
# Using awk for regex
echo "1 2 3 4 5" | awk '/[0-9]/ {print $1}'
# Using sed for replacement
echo "Hello World" | sed 's/World/Shell/g'
# Pattern matching in Bash
if [[ "hello" == h* ]]; then
echo "Starts with h"
fiShell supports parallel computing through background processes, xargs, GNU parallel, and job control.
- Background:
command & - wait:
wait $pid - xargs:
xargs -P4 - GNU parallel:
parallel -j4 - Subshells:
( command ) &
#!/bin/bash
# Parallel Computing in Shell
# Using background processes
parallel_task() {
sleep 2
echo "Task completed"
}
# Run in background
parallel_task &
parallel_task &
wait
echo "All tasks completed"
# Using xargs for parallel processing
echo {1..10} | xargs -n1 -P4 -I{} echo "Processing {}"
# Using GNU parallel
# parallel -j4 "echo Processing {}" ::: {1..10}
# Using subshells for parallel tasks
(
sleep 2
echo "Task 1 done"
) &
(
sleep 1
echo "Task 2 done"
) &
wait
# Using make for parallel execution
# make -j4
# Using for loops with &
for i in {1..10}; do
(
sleep 1
echo "Processing $i"
) &
done
wait
# Using while loop with background
while read -r line; do
(
echo "Processing $line"
) &
done < input.txt
wait
# Using named pipes (FIFO)
mkfifo pipe
(
echo "Data" > pipe
) &
read data < pipe
rm pipe
# Using parallel for file processing
find . -name "*.txt" -print0 | xargs -0 -P4 -I{} echo "Processing {}"Shell supports metaprogramming through eval, dynamic variable names, and code generation.
- eval:
eval "echo Hello" - Dynamic variables:
{!var_name} - Dynamic functions:
eval "func() { echo Hi; }" - Code generation: Using here-documents
- Source:
source generated.sh
#!/bin/bash
# Metaprogramming in Shell
# Using eval for dynamic code execution
code='echo "Hello World"'
eval "$code"
# Dynamic function calls
function add() {
echo $(( $1 + $2 ))
}
func_name="add"
$func_name 5 3
# Dynamic variable names
var_name="x"
eval "$var_name=10"
echo $x
# Using indirect expansion
var="name"
name="Alice"
echo "${!var}"
# Creating functions dynamically
eval 'my_function() { echo "Dynamic function"; }'
my_function
# Using here-documents for code generation
cat << 'EOF' > generated.sh
#!/bin/bash
echo "Generated script"
EOF
chmod +x generated.sh
./generated.sh
# Using source for dynamic loading
source ./config.sh
# Using declare for dynamic variables
declare -a dynamic_array
for i in {1..5}; do
dynamic_array+=("$i")
done
# Using alias for dynamic commands
alias my_cmd='echo "Hello"'
my_cmd
# Using trap for dynamic behavior
trap 'echo "Interrupted"' INT
# Using ${!var} for indirect references
a="Hello"
b="a"
echo "${!b}"Shell can interface with C programs by compiling and running them, or by using system calls.
- Compile:
gcc -o program program.c - Run:
./program - Pass arguments:
./program arg1 arg2 - Check exit code:
$? - Environment:
export VAR="value"
#!/bin/bash
# Interoperability with C in Shell
# Compiling C program
# gcc -o program program.c
# Running C program
./program
# Passing arguments to C program
./program arg1 arg2
# Using system calls from C
# system("ls -la");
# Using popen to read C program output
./program | while read -r line; do
echo "$line"
done
# Using C program with pipes
./program | grep "pattern"
# Creating a C shared library
# gcc -shared -o libmylib.so mylib.c
# Calling C library from shell (using dlopen)
# Not directly supported
# Using C program exit codes
./program
if [ $? -eq 0 ]; then
echo "Success"
else
echo "Failed"
fi
# Passing environment variables to C program
export MY_VAR="value"
./program
# Using C program with file I/O
./program input.txt output.txt
# Compiling and running in one command
gcc -o program program.c && ./program
# Using C program with signals
./program &
kill -SIGUSR1 $!Shell performance can be optimized through built-in commands, avoiding subshells, and using efficient loops.
- Built-ins: Use shell built-ins vs external
- Avoid subshells: Use
pushdvs(cd ...) - Arrays: Use arrays for collections
- Process substitution:
<(command) - Brace expansion:
{1..1000}
#!/bin/bash
# Performance Optimization in Shell
# Performance tips
# 1. Use built-in commands instead of external
# Instead of: ls | wc -l
# Use: echo "${#files[@]}"
# 2. Use bash built-ins
# Instead of: echo "Hello" | grep "H"
# Use: [[ "Hello" =~ H ]]
# 3. Avoid subshells when possible
# Instead of: (cd /tmp && ls)
# Use: pushd /tmp && ls && popd
# 4. Use arrays instead of strings
files=()
for file in *; do
files+=("$file")
done
# 5. Use process substitution
while read -r line; do
echo "$line"
done < <(find . -name "*.txt")
# 6. Use brace expansion
for i in {1..1000}; do
echo $i
done
# 7. Use arithmetic evaluation
sum=0
for i in {1..1000}; do
((sum += i))
done
# 8. Use nullglob
shopt -s nullglob
for file in *.txt; do
echo "$file"
done
# 9. Use bash's read with -r
while IFS= read -r line; do
echo "$line"
done < file.txt
# 10. Use exec for redirecting output
exec > output.log 2>&1
# 11. Use ulimit for resource limits
ulimit -n 4096
# 12. Profile with time
time ./script.sh
# 13. Use set -o pipefail for pipelines
set -o pipefail
# 14. Use compgen for completion
compgen -c | head -10
# 15. Use shopt for shell options
shopt -s extglobShell provides networking through curl, wget, nc, and other network tools.
- curl:
curl -s URL - wget:
wget -qO- URL - netcat:
nc -zv host port - SSH:
ssh user@host command - SCP:
scp local.txt user@host:/remote/
#!/bin/bash
# Networking in Shell
# HTTP GET request using curl
fetch_data() {
curl -s "$1"
}
# Example
# data=$(fetch_data "https://api.github.com")
# echo "$data"
# HTTP POST request
post_data() {
curl -s -X POST -H "Content-Type: application/json" -d "$2" "$1"
}
# Using wget
wget -O output.html https://example.com
# Using nc (netcat) for TCP
nc -zv example.com 80
if [ $? -eq 0 ]; then
echo "Port 80 is open"
fi
# TCP client
send_tcp() {
local host=$1
local port=$2
local message=$3
echo "$message" | nc "$host" "$port"
}
# TCP server (using nc)
# nc -l -p 8080 -e /bin/bash
# DNS resolution
host example.com
nslookup example.com
dig example.com
# Ping
ping -c 4 example.com
# Traceroute
traceroute example.com
# Using ssh for remote commands
# ssh user@host "command"
# Using scp for file transfer
# scp local.txt user@host:/remote/
# Using rsync for synchronization
# rsync -av source/ destination/
# Using nmap for port scanning
# nmap -p 1-1000 example.com
# Using telnet for testing
# telnet example.com 80Shell works with JSON using jq for parsing, manipulation, and generation of JSON data.
- Parse:
echo '{"name":"Alice"}' | jq '.name' - Pretty print:
echo '{"name":"Alice"}' | jq '.' - Generate:
jq -n '{name: "Bob"}' - Arrays:
echo '[1,2,3]' | jq '.[]' - Nested:
echo '{"user":{"name":"Alice"}}' | jq '.user.name'
#!/bin/bash
# Working with JSON in Shell
# Using jq
data='{"name":"Alice","age":25,"city":"NYC","hobbies":["reading","coding"]}'
# Parse JSON
echo "$data" | jq '.name'
echo "$data" | jq '.age'
echo "$data" | jq '.hobbies[0]'
# Pretty print
echo "$data" | jq '.'
# Create JSON
jq -n '{name: "Bob", age: 30, city: "LA"}'
# Array to JSON
echo '[1,2,3,4,5]' | jq '.'
# Nested JSON
nested='{"user":{"id":1,"profile":{"name":"Alice","email":"alice@example.com"}}}'
echo "$nested" | jq '.user.profile.name'
# Read JSON from file
# cat data.json | jq '.'
# Write JSON to file
echo '{"name":"Alice","age":25}' | jq '.' > output.json
# Using jq with variables
name="Bob"
age=30
echo "{}" | jq --arg n "$name" --arg a "$age" '.name=$n | .age=$a'
# JSON arrays
echo '["apple","banana","orange"]' | jq '.[]'
# JSON transformation
echo '{"name":"Alice","age":25}' | jq '{fullname: .name, years: .age}'
# Error handling
if echo "$data" | jq empty 2>/dev/null; then
echo "Valid JSON"
else
echo "Invalid JSON"
fiShell testing can be done with custom assert functions, BATS (Bash Automated Testing System), or shunit2.
- Custom asserts:
assert() { [ "$1" = "$2" ]; } - BATS:
@test "name" { [ ... ]; } - shunit2:
test_addition() { assertEquals ...; } - Test suites: Group tests
- Coverage:
bashcov
#!/bin/bash
# Testing in Shell
# Using bash unit test frameworks
# Simple assert function
assert() {
if [ "$1" = "$2" ]; then
echo "PASS: $3"
return 0
else
echo "FAIL: $3 - Expected: $1, Got: $2"
return 1
fi
}
# Test function
test_addition() {
result=$(add 2 2)
assert 4 "$result" "2 + 2 = 4"
}
# Test with floating point
test_float() {
local result=$(echo "0.1 + 0.2" | bc)
local expected="0.3"
if (( $(echo "$result == $expected" | bc -l) )); then
echo "PASS: Float test"
else
echo "FAIL: Float test - Expected: $expected, Got: $result"
fi
}
# Test with exit code
test_exit_code() {
./script.sh
if [ $? -eq 0 ]; then
echo "PASS: Exit code test"
else
echo "FAIL: Exit code test - Expected: 0, Got: $?"
fi
}
# Test suite
run_tests() {
test_addition
test_float
test_exit_code
}
# Using BATS (Bash Automated Testing System)
# https://github.com/bats-core/bats-core
# BATS test example
# @test "addition works" {
# result=$(add 2 2)
# [ "$result" -eq 4 ]
# }
# Using shunit2
# source shunit2
# test_addition() {
# assertEquals "2 + 2 = 4" 4 "$(add 2 2)"
# }
# shunit2
# Running tests
# run_tests
# bats test.bats
# ./test.shShell provides debugging through trace mode (set -x), verbose mode, and various debugging tools.
- set -x: Trace execution
- set -v: Verbose mode
- echo: Print debug messages
- trap: Error handling
- shellcheck: Static analysis
#!/bin/bash
# Debugging in Shell
# Using echo for debugging
debug_function() {
echo "Entering function with x=$1" >&2
result=$(( $1 * 2 ))
echo "Result = $result" >&2
echo $result
}
debug_function 5
# Using set -x for trace mode
set -x
# Code here will be traced
set +x
# Using set -v for verbose mode
set -v
# Code here will be displayed
set +v
# Using PS4 for custom trace output
export PS4='+ $BASH_SOURCE:$LINENO: '
set -x
# Using trap for error handling
trap 'echo "Error on line $LINENO"' ERR
# Using logger for system logging
logger "Debug message"
logger -t myscript "Debug message with tag"
# Using tee for output capture
echo "Hello" | tee -a debug.log
# Using exec for output redirection
exec 2> debug.log
# Using bashdb for debugging
# bashdb script.sh
# Using shellcheck for static analysis
# shellcheck script.sh
# Using set -e to exit on error
set -e
# Using set -u for undefined variables
set -u
# Using set -o pipefail for pipe errors
set -o pipefail
# Using bash -n for syntax checking
# bash -n script.sh
# Using bash -v for verbose execution
# bash -v script.sh
# Using xtrace with xargs
# bash -x script.sh
# Using debug trap
trap 'echo "Line $LINENO: $BASH_COMMAND"' DEBUGShell doesn't have formal abstract types, but functions and case statements can simulate abstract behaviors.
- Functions: Define interfaces
- Case statements: Polymorphic dispatch
- Arrays: Store behaviors
- Type checking:
declare -f - Inheritance: Function composition
#!/bin/bash
# Abstract Types and Interfaces in Shell
# Using functions as abstract methods
# Define abstract class pattern
animal() {
local action=$1
shift
case $action in
make_sound) echo "Animal sound";;
*) echo "Unknown action";;
esac
}
# Concrete implementations
dog() {
local action=$1
shift
case $action in
make_sound) echo "Woof!";;
*) animal "$action" "$@";;
esac
}
cat() {
local action=$1
shift
case $action in
make_sound) echo "Meow!";;
*) animal "$action" "$@";;
esac
}
# Interface-like using functions
sound_maker() {
make_sound "$@"
}
# Usage
dog make_sound
cat make_sound
# Using type checking
is_animal() {
declare -f "$1" > /dev/null
}
# Using inheritance simulation
sparrow() {
local action=$1
shift
case $action in
make_sound) echo "Chirp!";;
*) animal "$action" "$@";;
esac
}
# Checking object type
if is_animal dog; then
echo "Dog is an animal"
fi
# Using arrays for polymorphism
animals=(dog cat sparrow)
for animal in "${animals[@]}"; do
$animal make_sound
doneShell doesn't have formal generic types, but generic-like behavior can be simulated with arrays and functions.
- Generic collections: Use arrays
- Generic functions: Accept arrays as arguments
- Map: Apply function to each element
- Filter: Select by predicate
- Reduce: Aggregate values
#!/bin/bash
# Generic Types in Shell
# Using arrays for generic collections
declare -a list
# Generic list functions
list_add() {
local -n arr=$1
arr+=("$2")
}
list_get() {
local -n arr=$1
echo "${arr[$2]}"
}
list_length() {
local -n arr=$1
echo "${#arr[@]}"
}
# Generic map
map() {
local -n arr=$1
local func=$2
local result=()
for item in "${arr[@]}"; do
result+=($($func "$item"))
done
echo "${result[@]}"
}
# Generic filter
filter() {
local -n arr=$1
local pred=$2
local result=()
for item in "${arr[@]}"; do
if $pred "$item"; then
result+=("$item")
fi
done
echo "${result[@]}"
}
# Generic reduce
reduce() {
local -n arr=$1
local func=$2
local acc=$3
for item in "${arr[@]}"; do
acc=$($func "$acc" "$item")
done
echo "$acc"
}
# Usage
numbers=(1 2 3 4 5)
list_add numbers 6
list_get numbers 0
list_length numbers
# Generic functions
double() { echo $(( $1 * 2 )); }
is_even() { [ $(( $1 % 2 )) -eq 0 ]; }
doubled=$(map numbers double)
evens=$(filter numbers is_even)
sum=$(reduce numbers add 0)Traits in shell can be simulated using functions and composition patterns.
- Functions as traits: Define reusable behaviors
- Composition: Combine multiple traits
- Mixins: Add behavior to objects
- Method override: Redefine functions
- Validation: Trait with validation
#!/bin/bash
# Traits and Composition in Shell
# Using functions as traits
Logger() {
log() {
echo "[LOG] $1"
}
error() {
echo "[ERROR] $1" >&2
}
}
# Trait with properties
User() {
local name=""
local age=0
set_name() { name=$1; }
get_name() { echo "$name"; }
set_age() { age=$1; }
get_age() { echo "$age"; }
}
# Composition
UserWithLogger() {
# Compose User and Logger
User
Logger
# Override methods if needed
get_info() {
echo "Name: $(get_name), Age: $(get_age)"
}
}
# Usage
user_with_logger=UserWithLogger
$user_with_logger set_name "Alice"
$user_with_logger set_age 25
$user_with_logger get_info
$user_with_logger log "User created"
# Using mixins with arrays
mixins=()
add_mixin() {
mixins+=("$1")
}
apply_mixins() {
for mixin in "${mixins[@]}"; do
$mixin
done
}
# Trait for validation
Validator() {
validate_name() {
[[ -n $1 ]] && return 0 || return 1
}
validate_age() {
[[ $1 -gt 0 ]] && return 0 || return 1
}
}
# Composition with validation
UserWithValidation() {
User
Validator
set_name() {
if validate_name "$1"; then
name=$1
else
echo "Invalid name" >&2
fi
}
}Generators in shell can be implemented using functions with state, or using FIFOs and process substitution.
- Stateful functions: Track state in variables
- FIFOs: Named pipes for streaming
- Process substitution:
<(command) - Background jobs: Coroutine-like behavior
- Lazy evaluation: Using temporary files
#!/bin/bash
# Generators and Coroutines in Shell
# Generator using functions with state
counter_generator() {
local count=0
next() {
echo $((++count))
}
}
# Using generator
counter=counter_generator
$counter next
$counter next
$counter next
# Fibonacci generator
fibonacci_generator() {
local a=0
local b=1
next() {
local c=$a
a=$b
b=$((c + b))
echo $c
}
}
fib=fibonacci_generator
for i in {1..10}; do
$fib next
done
# Coroutine using named pipes
coroutine() {
local pipe="/tmp/coro_$$"
mkfifo "$pipe"
(
echo "Coroutine started"
sleep 2
echo "Coroutine finished"
) > "$pipe" &
cat "$pipe"
rm "$pipe"
}
# Using process substitution for coroutines
coproc my_coro {
while read -r cmd; do
case $cmd in
start) echo "Starting";;
stop) echo "Stopping"; break;;
*) echo "Unknown: $cmd";;
esac
done
}
# Lazy evaluation using temporary files
lazy_eval() {
local tmp=$(mktemp)
(
echo "Computing..."
sleep 2
echo "42" > "$tmp"
) &
cat "$tmp"
rm "$tmp"
}
# Using background jobs as coroutines
job() {
local name=$1
for i in {1..5}; do
echo "$name: $i"
sleep 1
done
}
job "A" &
job "B" &
waitShell provides basic array operations, and advanced operations can be implemented using loops and custom functions.
- Matrix ops: Nested arrays
- Element-wise: Loops
- Transpose: Swap indices
- Multiplication: Dot products
- Norm/Trace: Sum calculations
#!/bin/bash
# Advanced Array Operations
# Array initialization
declare -A matrix
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
matrix[$i,$j]=$((i*3+j+1))
done
done
# Reshaping (flatten)
flatten() {
local -n arr=$1
for key in "${!arr[@]}"; do
echo "${arr[$key]}"
done
}
# Transpose
transpose() {
local -n A=$1
local -n B=$2
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
B[$j,$i]=${A[$i,$j]}
done
done
}
# Element-wise operations
declare -A A
declare -A B
declare -A C
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
A[$i,$j]=$((i*3+j+1))
B[$i,$j]=$((A[$i,$j] + 1))
C[$i,$j]=$((A[$i,$j] * 2))
done
done
# Matrix multiplication
mat_mul() {
local -n A=$1
local -n B=$2
local -n R=$3
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
sum=0
for ((k=0; k<3; k++)); do
sum=$((sum + A[$i,$k] * B[$k,$j]))
done
R[$i,$j]=$sum
done
done
}
# Element-wise multiplication
elementwise_mul() {
local -n A=$1
local -n B=$2
local -n R=$3
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
R[$i,$j]=$((A[$i,$j] * B[$i,$j]))
done
done
}
# Matrix norm (Frobenius)
norm() {
local -n A=$1
local sum=0
for ((i=0; i<3; i++)); do
for ((j=0; j<3; j++)); do
sum=$((sum + A[$i,$j] * A[$i,$j]))
done
done
echo "scale=2; sqrt($sum)" | bc
}
# Trace
trace() {
local -n A=$1
local sum=0
for ((i=0; i<3; i++)); do
sum=$((sum + A[$i,$i]))
done
echo $sum
}
# Diagonal
diag() {
local -n A=$1
for ((i=0; i<3; i++)); do
echo "${A[$i,$i]}"
done
}Shell handles missing data using empty strings, {var:-default}, and existence checks.
- Empty strings: Represent null
- Default values:
{var:-default} - Existence check:
[[ -v var ]] - Remove: Filter out empties
- Replace:
{var:-default}
#!/bin/bash
# Handling Missing Data (Null handling)
# Creating arrays with missing values
data=("1" "2" "" "4" "5" "" "7")
# Check for missing values
has_missing() {
for item in "${@}"; do
if [ -z "$item" ]; then
return 0
fi
done
return 1
}
# Remove missing values
remove_missing() {
local result=()
for item in "${@}"; do
if [ -n "$item" ]; then
result+=("$item")
fi
done
echo "${result[@]}"
}
# Replace missing values
replace_missing() {
local default=$1
shift
local result=()
for item in "${@}"; do
if [ -n "$item" ]; then
result+=("$item")
else
result+=("$default")
fi
done
echo "${result[@]}"
}
# Operations with missing values
x=("1" "2" "" "4")
y=("5" "6" "" "8")
z=()
for i in "${!x[@]}"; do
if [ -n "${x[$i]}" ] && [ -n "${y[$i]}" ]; then
z+=($((x[i] + y[i])))
else
z+=("")
fi
done
# Ignoring missing values
sum_complete=0
for item in "${x[@]}"; do
if [ -n "$item" ]; then
sum_complete=$((sum_complete + item))
fi
done
# Using default values
value="${data[10]:-default}"
# Checking for unset variables
if [ -z "${var+x}" ]; then
echo "var is unset"
fi
# Null coalescing (Bash 4+)
var=${var:-default}
# Using parameter expansion
name="${name:-Default}"Shell provides sorting through sort and searching through grep, awk, and loops.
- sort:
sort -n - Custom sort:
sort -k2 - Search:
grep pattern - Binary search: Custom implementation
- Contains:
[[ $item =~ pattern ]]
#!/bin/bash
# Sorting and Searching
# Basic sorting
arr=(5 2 8 1 9 3)
sorted=($(printf '%s
' "${arr[@]}" | sort -n))
echo "${sorted[@]}"
# Sorting with custom comparator
arr2=("5 apple" "3 banana" "8 cherry")
sorted2=($(printf '%s
' "${arr2[@]}" | sort -n))
echo "${sorted2[@]}"
# Sorting descending
arr3=(5 2 8 1 9 3)
sorted3=($(printf '%s
' "${arr3[@]}" | sort -nr))
echo "${sorted3[@]}"
# Search functions
arr4=(1 3 5 7 9 11)
greater_than_5=()
for item in "${arr4[@]}"; do
if [ $item -gt 5 ]; then
greater_than_5+=("$item")
fi
done
echo "${greater_than_5[@]}"
# First greater than 5
first_greater=()
for item in "${arr4[@]}"; do
if [ $item -gt 5 ]; then
first_greater="$item"
break
fi
done
echo "$first_greater"
# Last greater than 5
last_greater=()
for item in "${arr4[@]}"; do
if [ $item -gt 5 ]; then
last_greater="$item"
fi
done
echo "$last_greater"
# Contains
has_seven=0
has_four=0
for item in "${arr4[@]}"; do
if [ $item -eq 7 ]; then
has_seven=1
fi
if [ $item -eq 4 ]; then
has_four=1
fi
done
echo "Has 7: $has_seven, Has 4: $has_four"
# Binary search
binary_search() {
local arr=("$@")
local target=${arr[-1]}
unset 'arr[-1]'
local left=0
local right=$((${#arr[@]} - 1))
while [ $left -le $right ]; do
local mid=$(((left + right) / 2))
if [ ${arr[$mid]} -eq $target ]; then
echo $mid
return
elif [ ${arr[$mid]} -lt $target ]; then
left=$((mid + 1))
else
right=$((mid - 1))
fi
done
echo -1
}
arr5=(1 2 3 4 5 6 7)
index=$(binary_search "${arr5[@]}" 5)
echo "Found at index: $index"Shell provides arithmetic operations through $((...)), bc, and awk.
- Arithmetic:
$((1+2)) - bc:
echo "scale=2; 10/3" | bc - Trigonometric:
bc -l - Random:
$RANDOM - Statistics: Custom functions
#!/bin/bash
# Mathematical Operations
# Basic arithmetic
x=10
y=3
echo "$((x + y))"
echo "$((x - y))"
echo "$((x * y))"
echo "$((x / y))"
echo "$((x % y))"
echo "$((x ** y))"
# Mathematical functions (using bc)
echo "scale=4; s(3.14159/4)" | bc -l
echo "scale=4; c(3.14159/4)" | bc -l
echo "scale=4; e(1)" | bc -l
echo "scale=4; l(e(1))" | bc -l
echo "scale=4; sqrt(9)" | bc -l
# Special functions
echo "abs(-5) = ${abs#-}"
ceil() {
echo "$1" | awk '{print ($0 == int($0)) ? $0 : int($0) + 1}'
}
floor() {
echo "$1" | awk '{print ($0 == int($0)) ? $0 : int($0) - 1}'
}
round() {
echo "$1" | awk '{print int($1 + 0.5)}'
}
# Random numbers
echo $((RANDOM % 10 + 1))
echo $((RANDOM % 100))
# Statistics
data=(1 2 3 4 5 6 7 8 9 10)
sum=0
for i in "${data[@]}"; do
sum=$((sum + i))
done
echo "sum = $sum"
echo "mean = $(echo "scale=2; $sum / ${#data[@]}" | bc)"
# Min and Max
min=${data[0]}
max=${data[0]}
for i in "${data[@]}"; do
[ $i -lt $min ] && min=$i
[ $i -gt $max ] && max=$i
done
echo "min = $min, max = $max"Shell serializes data using text formats like CSV, JSON, YAML, or custom formats with base64 encoding.
- CSV: Comma-separated values
- JSON: Using
jq - YAML: Using
yq - base64:
echo "data" | base64 - Custom: Key-value pairs
#!/bin/bash
# Data Serialization
# Using echo and cat for simple serialization
data="name:Alice,age:25,city:NYC"
echo "$data" > data.txt
deserialized=$(cat data.txt)
echo "$deserialized"
# Using JSON with jq
echo '{"name":"Alice","age":25}' > data.json
cat data.json | jq '.'
# Using CSV
echo "name,age,city" > data.csv
echo "Alice,25,NYC" >> data.csv
echo "Bob,30,LA" >> data.csv
# Using YAML (requires yq)
# yq eval '.name' data.yaml
# Using XML (requires xmlstarlet)
# xmlstarlet sel -t -v "/root/name" data.xml
# Using base64 encoding
encoded=$(echo "Hello World" | base64)
decoded=$(echo "$encoded" | base64 -d)
# Using tar for archiving
tar -czf archive.tar.gz file1 file2
# Using gzip compression
gzip -c data.txt > data.txt.gz
gunzip -c data.txt.gz > data.txt
# Using serialize/deserialize functions
serialize() {
echo "$1" | base64
}
deserialize() {
echo "$1" | base64 -d
}
# Using arrays for serialization
declare -A person
person[name]="Alice"
person[age]=25
person[city]="NYC"
# Serialize associative array
for key in "${!person[@]}"; do
echo "$key=${person[$key]}"
done > person.dat
# Deserialize
while IFS='=' read -r key value; do
person2["$key"]="$value"
done < person.datShell interfaces with external systems through database clients, HTTP tools, SSH, and system commands.
- Database:
sqlite3,mysql - HTTP:
curl,wget - SSH:
ssh user@host - System:
command - Environment:
export
#!/bin/bash
# Interfacing with External Systems
# Database connections (using sqlite3)
sqlite3 database.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"
sqlite3 database.db "INSERT INTO users (name) VALUES ('Alice')"
sqlite3 database.db "SELECT * FROM users"
# Using MySQL
# mysql -u user -p -e "SELECT * FROM users" database
# Using PostgreSQL
# psql -U user -d database -c "SELECT * FROM users"
# Using Redis (with redis-cli)
# redis-cli set key value
# redis-cli get key
# Using HTTP requests
curl -X GET https://api.github.com
curl -X POST -H "Content-Type: application/json" -d '{"name":"Alice"}' https://httpbin.org/post
# Using SSH
# ssh user@host "command"
# Using SCP
# scp local.txt user@host:/remote/
# Using rsync
# rsync -av source/ destination/
# Using system calls
system() {
command "$@"
}
# Executing shell commands
output=$(ls -la)
echo "$output"
# Using environment variables
export MY_VAR="value"
echo $MY_VAR
# Using temporary files
temp_file=$(mktemp)
echo "data" > "$temp_file"
rm "$temp_file"
# Using named pipes
mkfifo pipe
echo "Hello" > pipe &
cat pipe
rm pipeReverse a string by iterating from the end to the beginning.
- Method:
for ((i=${#s}-1; i>=0; i--)); do reversed="${reversed}${s:$i:1}"; done - Alternative:
revcommand - Time: O(n)
- Edge cases: Empty string
#!/bin/bash
# Reverse a string
reverse_string() {
local s="$1"
local reversed=""
for ((i=${#s}-1; i>=0; i--)); do
reversed="${reversed}${s:$i:1}"
done
echo "$reversed"
}
reverse_string_manual() {
local s="$1"
local len=${#s}
local reversed=""
for ((i=0; i<len; i++)); do
reversed="${s:$i:1}$reversed"
done
echo "$reversed"
}
s="hello"
echo "Original: $s"
echo "Reversed: $(reverse_string "$s")"
echo "Reversed (manual): $(reverse_string_manual "$s")"Check if a string is a palindrome by comparing it with its reverse.
- Method:
s == $(reverse_string "$s") - Case insensitive:
{s,,} - Ignore spaces:
{s// /} - Recursive: Compare ends
#!/bin/bash
# Check palindrome
is_palindrome() {
local s="${1,,}"
local cleaned="${s// /}"
local reversed=$(reverse_string "$cleaned")
[ "$cleaned" = "$reversed" ]
}
is_palindrome_manual() {
local s="${1,,}"
local cleaned="${s// /}"
local len=${#cleaned}
for ((i=0; i<len/2; i++)); do
if [ "${cleaned:$i:1}" != "${cleaned:$len-$i-1:1}" ]; then
return 1
fi
done
return 0
}
strings=("racecar" "hello" "A man a plan a canal Panama" "race a car")
for s in "${strings[@]}"; do
if is_palindrome "$s"; then
echo ""$s" is palindrome"
else
echo ""$s" is not palindrome"
fi
doneFind the maximum value by iterating through the array.
- Method:
max={arr[0]}; for val in "{arr[@]}"; do [ $val -gt $max ] && max=$val; done - Alternative:
printf '%s\n' "{arr[@]}" | sort -n | tail -1 - Empty: Handle empty case
- Time: O(n)
#!/bin/bash
# Find max in array
find_max() {
local max=$1
shift
for val in "$@"; do
if [ $val -gt $max ]; then
max=$val
fi
done
echo $max
}
find_max_manual() {
local arr=("$@")
local max=${arr[0]}
for ((i=1; i<${#arr[@]}; i++)); do
if [ ${arr[$i]} -gt $max ]; then
max=${arr[$i]}
fi
done
echo $max
}
arr=(1 5 3 9 2)
echo "Array: ${arr[@]}"
echo "Max: $(find_max "${arr[@]}")"
echo "Max (manual): $(find_max_manual "${arr[@]}")"Remove duplicates by tracking seen items and skipping duplicates.
- Method:
printf '%s\n' "{arr[@]}" | sort -u - Manual:
seen=(); for item in "{arr[@]}"; do ... - Preserve order: Manual method
- Time: O(n²) or O(n log n) with sort
#!/bin/bash
# Remove duplicates
remove_duplicates() {
local result=()
local seen=()
for item in "$@"; do
local found=0
for seen_item in "${seen[@]}"; do
if [ "$seen_item" = "$item" ]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
seen+=("$item")
result+=("$item")
fi
done
echo "${result[@]}"
}
remove_duplicates_sort() {
printf '%s
' "$@" | sort -u
}
arr=("apple" "banana" "apple" "orange" "banana" "grape")
echo "Original: ${arr[@]}"
echo "Without duplicates: $(remove_duplicates "${arr[@]}")"
echo "Without duplicates (sort): $(remove_duplicates_sort "${arr[@]}")"Merge arrays by concatenating or merging sorted arrays.
- Concat:
result=("{arr1[@]}" "{arr2[@]}") - Sorted merge: Compare and add
- Unique:
printf '%s\n' "{arr1[@]}" "{arr2[@]}" | sort -u - Time: O(n+m)
#!/bin/bash
# Merge arrays
merge_arrays() {
echo "${@}"
}
merge_sorted() {
local arr1=("${!1}")
local arr2=("${!2}")
local result=()
local i=0 j=0
while [ $i -lt ${#arr1[@]} ] && [ $j -lt ${#arr2[@]} ]; do
if [ ${arr1[$i]} -le ${arr2[$j]} ]; then
result+=(${arr1[$i]})
((i++))
else
result+=(${arr2[$j]})
((j++))
fi
done
while [ $i -lt ${#arr1[@]} ]; do
result+=(${arr1[$i]})
((i++))
done
while [ $j -lt ${#arr2[@]} ]; do
result+=(${arr2[$j]})
((j++))
done
echo "${result[@]}"
}
arr1=(1 2 3)
arr2=(4 5 6)
echo "Merged: $(merge_arrays "${arr1[@]}" "${arr2[@]}")"
sorted1=(1 3 5 7)
sorted2=(2 4 6 8)
echo "Merged sorted: $(merge_sorted sorted1 sorted2)"Convert a string to a number using arithmetic expansion or bc.
- Integer:
$((var)) - Float:
echo "$var" | bc - Safe:
${var//[^0-9.-]/} - Error handling:
2>/dev/null
#!/bin/bash
# Convert string to number
string_to_number() {
echo "$1" | bc 2>/dev/null || echo 0
}
string_to_int() {
echo "${1//[^0-9-]/}" | bc
}
string_to_float() {
echo "$1" | bc -l 2>/dev/null || echo 0
}
strings=("42" "3.14" "hello" "123" "45.67")
for s in "${strings[@]}"; do
echo ""$s" -> int: $(string_to_int "$s"), float: $(string_to_float "$s")"
doneIterate through an associative array using for key in "{!dict[@]}".
- Keys:
{!dict[@]} - Values:
{dict[$key]} - Find key: Check if key exists
- Return:
{dict[$key]:-default}
#!/bin/bash
# Loop through associative array
declare -A data
data[name]="Alice"
data[age]=25
data[city]="NYC"
loop_dict() {
for key in "${!data[@]}"; do
echo "$key => ${data[$key]}"
done
}
find_key() {
local key=$1
echo "${data[$key]:-Not found}"
}
echo "Dictionary:"
loop_dict
echo ""
name=$(find_key "name")
echo "Name: $name"
country=$(find_key "country")
echo "Country: $country"Delay execution using sleep or background processes.
- Blocking:
sleep $seconds; command - Async:
(sleep $seconds; command) & - Callback: Function after delay
- Timer: Use
atorcron
#!/bin/bash
# Delay function execution
delay_seconds() {
sleep "$1"
shift
"$@"
}
delay_async() {
(
sleep "$1"
shift
"$@"
) &
}
delayed_print() {
echo "Starting delay of $2 seconds"
delay_seconds "$2" echo "$1"
}
echo "Delayed execution examples:"
delayed_print "After 2 seconds" 2
echo "Main script continues"Make HTTP requests using curl or wget.
- curl:
curl -s URL - wget:
wget -qO- URL - Headers:
curl -H "Header: value" - Error handling:
curl -f
#!/bin/bash
# HTTP GET request
fetch_data() {
curl -s "$1"
}
fetch_data_wget() {
wget -qO- "$1"
}
post_data() {
curl -s -X POST -H "Content-Type: application/json" -d "$2" "$1"
}
# Example
# result=$(fetch_data "https://api.github.com")
# echo "$result"
# Using wget
# result=$(fetch_data_wget "https://api.github.com")
# POST example
# result=$(post_data "https://httpbin.org/post" '{"name":"Alice","age":25}')
# Using curl with headers
curl -s -H "User-Agent: Bash" "https://api.github.com"
# Using curl with authentication
# curl -u username:password "https://api.github.com"
# Using curl for file download
# curl -O https://example.com/file.txt
# Using wget for file download
# wget https://example.com/file.txt
# Using httpie
# http GET https://api.github.com
# Error handling
if ! result=$(curl -s -f "https://api.github.com" 2>/dev/null); then
echo "Error fetching data"
fiCreate promise-like behavior using background processes and wait.
- Promise:
(sleep 1; echo "Success") & - Wait:
wait $pid - Check:
$?for success/failure - Chain: Sequential waits
#!/bin/bash
# Create a promise-like task
create_promise() {
local should_resolve=$1
local result=""
(
sleep 1
if [ "$should_resolve" = true ]; then
echo "Success!"
else
echo "Failed!"
exit 1
fi
) &
echo $!
}
# Using the promise
pid=$(create_promise true)
wait $pid
if [ $? -eq 0 ]; then
echo "Promise resolved"
else
echo "Promise rejected"
fi
# Promise with error handling
pid=$(create_promise false)
wait $pid
if [ $? -eq 0 ]; then
echo "Promise resolved"
else
echo "Promise rejected"
fi
# Chain promises
chain_promises() {
local p1=$1
local p2=$2
(
wait $p1
if [ $? -eq 0 ]; then
wait $p2
if [ $? -eq 0 ]; then
echo "Both completed"
else
echo "Second failed"
fi
else
echo "First failed"
fi
) &
}
# Promise all
promise_all() {
local pids=("$@")
for pid in "${pids[@]}"; do
wait $pid
if [ $? -ne 0 ]; then
echo "Failed"
return 1
fi
done
echo "All succeeded"
}Calculate factorial using recursion or iteration.
- Recursive:
factorial() { [ $1 -le 1 ] && echo 1 || echo $(( $1 * $(factorial $(( $1 - 1 )) ) )); } - Iterative:
for ((i=2; i<=n; i++)); do result=$((result * i)); done - Edge cases: 0! = 1
- Time: O(n)
#!/bin/bash
# Factorial
factorial() {
if [ $1 -le 1 ]; then
echo 1
else
local n=$1
local sub=$(factorial $((n - 1)))
echo $((n * sub))
fi
}
factorial_iterative() {
local n=$1
local result=1
for ((i=2; i<=n; i++)); do
result=$((result * i))
done
echo $result
}
n=5
echo "Factorial of $n:"
echo "Recursive: $(factorial $n)"
echo "Iterative: $(factorial_iterative $n)"Calculate Fibonacci numbers using recursion, iteration, or memoization.
- Recursive:
fib() { [ $1 -le 1 ] && echo $1 || echo $(( $(fib $(( $1 - 1 )) ) + $(fib $(( $1 - 2 )) ) )); } - Iterative:
for ((i=2; i<=n; i++)); do c=$((a+b)); a=$b; b=$c; done - Memoized: Use cache file
- Time: O(n) iterative
#!/bin/bash
# Fibonacci
fibonacci() {
if [ $1 -le 1 ]; then
echo $1
else
local n=$1
local a=$(fibonacci $((n - 1)))
local b=$(fibonacci $((n - 2)))
echo $((a + b))
fi
}
fibonacci_iterative() {
local n=$1
if [ $n -le 1 ]; then
echo $n
return
fi
local a=0 b=1
for ((i=2; i<=n; i++)); do
local c=$((a + b))
a=$b
b=$c
done
echo $b
}
fibonacci_memoized() {
local n=$1
local cache_file="/tmp/fib_cache_$$"
if [ -f "$cache_file" ] && grep -q "^$n:" "$cache_file"; then
grep "^$n:" "$cache_file" | cut -d: -f2
return
fi
local result
if [ $n -le 1 ]; then
result=$n
else
local a=$(fibonacci_memoized $((n - 1)))
local b=$(fibonacci_memoized $((n - 2)))
result=$((a + b))
fi
echo "$n:$result" >> "$cache_file"
echo $result
}
trap 'rm -f /tmp/fib_cache_$$' EXIT
n=10
echo "Fibonacci of $n:"
echo "Recursive: $(fibonacci $n)"
echo "Iterative: $(fibonacci_iterative $n)"
echo "Memoized: $(fibonacci_memoized $n)"Print numbers with FizzBuzz logic using conditional statements.
- If-else:
if [ $((i % 15)) -eq 0 ]; then ... - Case:
case $((i % 15)) in 0) echo "FizzBuzz";; ... - Array: Store results
- Output:
echo
#!/bin/bash
# FizzBuzz
fizzbuzz() {
local n=$1
for ((i=1; i<=n; i++)); do
if [ $((i % 15)) -eq 0 ]; then
echo "FizzBuzz"
elif [ $((i % 3)) -eq 0 ]; then
echo "Fizz"
elif [ $((i % 5)) -eq 0 ]; then
echo "Buzz"
else
echo $i
fi
done
}
fizzbuzz_array() {
local n=$1
local result=()
for ((i=1; i<=n; i++)); do
if [ $((i % 15)) -eq 0 ]; then
result+=("FizzBuzz")
elif [ $((i % 3)) -eq 0 ]; then
result+=("Fizz")
elif [ $((i % 5)) -eq 0 ]; then
result+=("Buzz")
else
result+=($i)
fi
done
echo "${result[@]}"
}
echo "FizzBuzz for 15:"
fizzbuzz 15
echo "FizzBuzz array:"
fizzbuzz_array 15Find missing number using sum formula or XOR operation.
- Sum:
$((n*(n+1)/2 - sum)) - XOR:
xor_all ^ xor_arr - Time: O(n)
- Edge cases: Empty array
#!/bin/bash
# Find missing number
find_missing() {
local arr=("$@")
local n=$((${#arr[@]} + 1))
local total=$((n * (n + 1) / 2))
local sum=0
for num in "${arr[@]}"; do
sum=$((sum + num))
done
echo $((total - sum))
}
find_missing_xor() {
local arr=("$@")
local n=$((${#arr[@]} + 1))
local xor_all=0
for ((i=1; i<=n; i++)); do
xor_all=$((xor_all ^ i))
done
local xor_arr=0
for num in "${arr[@]}"; do
xor_arr=$((xor_arr ^ num))
done
echo $((xor_all ^ xor_arr))
}
arr=(1 2 4 5 6)
echo "Missing number: $(find_missing "${arr[@]}")"
echo "Missing number (XOR): $(find_missing_xor "${arr[@]}")"Find duplicates using associative arrays or sort | uniq -d.
- Associative array: Count occurrences
- sort:
printf '%s\n' "{arr[@]}" | sort | uniq -d - Time: O(n) or O(n log n)
- Returns: Duplicate values
#!/bin/bash
# Find duplicates
find_duplicates() {
local arr=("$@")
local seen=()
local dups=()
for item in "${arr[@]}"; do
local found=0
for seen_item in "${seen[@]}"; do
if [ "$seen_item" = "$item" ]; then
found=1
break
fi
done
if [ $found -eq 1 ]; then
local dup_found=0
for dup in "${dups[@]}"; do
if [ "$dup" = "$item" ]; then
dup_found=1
break
fi
done
if [ $dup_found -eq 0 ]; then
dups+=("$item")
fi
else
seen+=("$item")
fi
done
echo "${dups[@]}"
}
find_duplicates_count() {
local arr=("$@")
declare -A count
for item in "${arr[@]}"; do
count[$item]=$((count[$item] + 1))
done
local dups=()
for key in "${!count[@]}"; do
if [ ${count[$key]} -gt 1 ]; then
dups+=("$key")
fi
done
echo "${dups[@]}"
}
arr=(1 2 3 2 4 3 5 6 5)
echo "Original: ${arr[@]}"
echo "Duplicates: $(find_duplicates "${arr[@]}")"
echo "Duplicates (count): $(find_duplicates_count "${arr[@]}")"Sum array elements using a loop or awk.
- Loop:
sum=0; for num in "{arr[@]}"; do sum=$((sum + num)); done - awk:
printf '%s\n' "{arr[@]}" | awk '{sum+=$1} END {print sum}' - Empty: Returns 0
- Time: O(n)
#!/bin/bash
# Sum of array
sum_array() {
local sum=0
for num in "$@"; do
sum=$((sum + num))
done
echo $sum
}
sum_array_manual() {
local arr=("$@")
local sum=0
for ((i=0; i<${#arr[@]}; i++)); do
sum=$((sum + arr[i]))
done
echo $sum
}
arr=(1 2 3 4 5)
echo "Array: ${arr[@]}"
echo "Sum: $(sum_array "${arr[@]}")"
echo "Sum (manual): $(sum_array_manual "${arr[@]}")"Calculate average by dividing sum by length.
- Method:
sum=0; count=0; for num in "{arr[@]}"; do sum=$((sum + num)); count=$((count + 1)); done; echo "scale=2; $sum / $count" | bc - Integer:
echo $((sum / count)) - Empty: Return 0
- Time: O(n)
#!/bin/bash
# Average of array
average_array() {
local sum=0
local count=0
for num in "$@"; do
sum=$((sum + num))
count=$((count + 1))
done
echo "scale=2; $sum / $count" | bc
}
average_integer() {
local sum=0
local count=0
for num in "$@"; do
sum=$((sum + num))
count=$((count + 1))
done
echo $((sum / count))
}
int_arr=(1 2 3 4 5)
float_arr=(1.0 2.0 3.0 4.0 5.0)
echo "Average (int array): $(average_array "${int_arr[@]}")"
echo "Average (integer): $(average_integer "${int_arr[@]}")"Sort arrays using sort -n or bubble sort.
- sort:
printf '%s\n' "{arr[@]}" | sort -n - Bubble sort: Custom implementation
- Time: O(n log n) with sort
- In-place:
IFS=$'\n' read -ra sorted <<< "$(printf '%s\n' "{arr[@]}" | sort -n)"
#!/bin/bash
# Sort ascending
sort_ascending() {
printf '%s
' "$@" | sort -n
}
sort_ascending_inplace() {
local arr=("$@")
IFS=$'\n' read -ra sorted <<< "$(printf '%s
' "${arr[@]}" | sort -n)"
arr=("${sorted[@]}")
}
arr=(5 2 8 1 9 3)
echo "Original: ${arr[@]}"
sorted=($(sort_ascending "${arr[@]}"))
echo "Sorted ascending: ${sorted[@]}"
sort_ascending_inplace "${arr[@]}"
echo "Sorted in-place: ${arr[@]}"Sort descending using sort -nr or custom sort.
- sort:
printf '%s\n' "{arr[@]}" | sort -nr - Bubble sort: Reverse comparison
- Time: O(n log n) with sort
- In-place: Similar to ascending
#!/bin/bash
# Sort descending
sort_descending() {
printf '%s
' "$@" | sort -nr
}
sort_descending_inplace() {
local arr=("$@")
IFS=$'\n' read -ra sorted <<< "$(printf '%s
' "${arr[@]}" | sort -nr)"
arr=("${sorted[@]}")
}
arr=(5 2 8 1 9 3)
echo "Original: ${arr[@]}"
sorted=($(sort_descending "${arr[@]}"))
echo "Sorted descending: ${sorted[@]}"
sort_descending_inplace "${arr[@]}"
echo "Sorted in-place: ${arr[@]}"Flatten nested arrays using recursion or echo.
- Recursive:
flatten() { for item in "$@"; do ...; done } - Simple:
echo "$@" - Time: O(n)
- Depth: Handles nested structures
#!/bin/bash
# Flatten nested array
flatten() {
local result=()
for item in "$@"; do
if [[ "$item" =~ ^[0-9]+$ ]]; then
result+=("$item")
else
local sub_result=($(flatten $item))
result+=("${sub_result[@]}")
fi
done
echo "${result[@]}"
}
# Simplified flatten using echo
flatten_simple() {
echo "$@" | tr ' ' '\n' | sort -u
}
nested="1 2 3 4 5 6 7 8 9 10"
deeper="1 2 3 4 5 6 7 8 9 10"
echo "Nested: $nested"
echo "Flatten: $(flatten_simple "$nested")"Split an array into chunks of a specified size.
- Method:
chunk_array() { local size=$1; shift; ... } - Loop:
while [ $i -lt {#arr[@]} ]; do ... - Use case: Batch processing
- Time: O(n)
#!/bin/bash
# Chunk array
chunk_array() {
local arr=("$@")
local size=$1
shift
local result=()
local chunk=()
local count=0
for item in "$@"; do
chunk+=("$item")
count=$((count + 1))
if [ $count -eq $size ]; then
result+=("${chunk[@]}")
chunk=()
count=0
fi
done
if [ ${#chunk[@]} -gt 0 ]; then
result+=("${chunk[@]}")
fi
echo "${result[@]}"
}
chunk_array_manual() {
local arr=("$@")
local size=$1
shift
local result=()
local i=0
while [ $i -lt ${#arr[@]} ]; do
local end=$((i + size))
if [ $end -gt ${#arr[@]} ]; then
end=${#arr[@]}
fi
local chunk=()
for ((j=i; j<end; j++)); do
chunk+=("${arr[$j]}")
done
result+=("${chunk[@]}")
i=$end
done
echo "${result[@]}"
}
arr=(1 2 3 4 5 6 7 8 9 10)
echo "Original: ${arr[@]}"
echo "Chunk (size 3):"
chunk_array 3 "${arr[@]}"Implement binary search on a sorted array.
- Method:
while [ $left -le $right ]; do mid=$(((left+right)/2)); ... - Precondition: Sorted array
- Time: O(log n)
- Returns: Index or -1
#!/bin/bash
# Binary search
binary_search() {
local arr=("$@")
local target=${arr[-1]}
unset 'arr[-1]'
local left=0
local right=$((${#arr[@]} - 1))
while [ $left -le $right ]; do
local mid=$(((left + right) / 2))
if [ ${arr[$mid]} -eq $target ]; then
echo $mid
return
elif [ ${arr[$mid]} -lt $target ]; then
left=$((mid + 1))
else
right=$((mid - 1))
fi
done
echo -1
}
arr=(1 2 3 4 5 6 7)
target=5
index=$(binary_search "${arr[@]}" $target)
echo "Found $target at index: $index"
target2=8
index2=$(binary_search "${arr[@]}" $target2)
echo "Found $target2 at index: $index2"Implement quick sort with pivot and recursion.
- Recursive:
quick_sort() { ... } - Pivot: First element
- Partition: Split into smaller/larger
- Time: O(n log n) average
#!/bin/bash
# Quick sort
quick_sort() {
local arr=("$@")
if [ ${#arr[@]} -le 1 ]; then
echo "${arr[@]}"
return
fi
local pivot=${arr[0]}
local left=()
local right=()
for ((i=1; i<${#arr[@]}; i++)); do
if [ ${arr[$i]} -lt $pivot ]; then
left+=(${arr[$i]})
else
right+=(${arr[$i]})
fi
done
local sorted_left=($(quick_sort "${left[@]}"))
local sorted_right=($(quick_sort "${right[@]}"))
echo "${sorted_left[@]} $pivot ${sorted_right[@]}"
}
arr=(5 3 8 4 2 7 1 6)
echo "Original: ${arr[@]}"
sorted=($(quick_sort "${arr[@]}"))
echo "Quick sort: ${sorted[@]}"Implement merge sort with divide and conquer.
- Divide: Split in half
- Merge: Combine sorted halves
- Time: O(n log n)
- Space: O(n)
#!/bin/bash
# Merge sort implementation
# Merge function - combines two sorted halves
merge() {
local left=("$@")
local right=()
local mid=$((${#left[@]} / 2))
# Split into left and right halves
for ((i=0; i<mid; i++)); do
right+=("${left[$i]}")
done
left=("${left[@]:mid}")
local result=()
local i=0 j=0
# Merge the two sorted halves
while [ $i -lt ${#left[@]} ] && [ $j -lt ${#right[@]} ]; do
if [ ${left[$i]} -le ${right[$j]} ]; then
result+=(${left[$i]})
i=$((i + 1))
else
result+=(${right[$j]})
j=$((j + 1))
fi
done
# Append remaining elements from left (if any)
while [ $i -lt ${#left[@]} ]; do
result+=(${left[$i]})
i=$((i + 1))
done
# Append remaining elements from right (if any)
while [ $j -lt ${#right[@]} ]; do
result+=(${right[$j]})
j=$((j + 1))
done
echo "${result[@]}"
}
# Main merge_sort function
merge_sort() {
local arr=("$@")
# Base case: array with 0 or 1 element is already sorted
if [ ${#arr[@]} -le 1 ]; then
echo "${arr[@]}"
return
fi
# Split array into two halves
local mid=$((${#left[@]} / 2))
local left=()
local right=()
for ((i=0; i<mid; i++)); do
left+=(${arr[$i]})
done
for ((i=mid; i<${#arr[@]}; i++)); do
right+=(${arr[$i]})
done
# Recursively sort both halves
local sorted_left=($(merge_sort "${left[@]}"))
local sorted_right=($(merge_sort "${right[@]}"))
# Merge the sorted halves
echo $(merge "${sorted_left[@]}" "${sorted_right[@]}")
}
# Usage example
arr=(5 3 8 4 2 7 1 6)
echo "Original: ${arr[@]}"
sorted=($(merge_sort "${arr[@]}"))
echo "Merge sort: ${sorted[@]}"Implement bubble sort with optimization.
- Basic:
for ((i=0; i<n-1; i++)); do for ((j=0; j<n-i-1; j++)); do if [ {arr[$j]} -gt {arr[$j+1]} ]; then swap; fi; done; done - Optimized: Track swaps
- Time: O(n²)
- Use case: Small arrays
#!/bin/bash
# Bubble sort
bubble_sort() {
local arr=("$@")
local n=${#arr[@]}
for ((i=0; i<n-1; i++)); do
for ((j=0; j<n-i-1; j++)); do
if [ ${arr[$j]} -gt ${arr[$j+1]} ]; then
local temp=${arr[$j]}
arr[$j]=${arr[$j+1]}
arr[$j+1]=$temp
fi
done
done
echo "${arr[@]}"
}
bubble_sort_optimized() {
local arr=("$@")
local n=${#arr[@]}
for ((i=0; i<n-1; i++)); do
local swapped=0
for ((j=0; j<n-i-1; j++)); do
if [ ${arr[$j]} -gt ${arr[$j+1]} ]; then
local temp=${arr[$j]}
arr[$j]=${arr[$j+1]}
arr[$j+1]=$temp
swapped=1
fi
done
if [ $swapped -eq 0 ]; then
break
fi
done
echo "${arr[@]}"
}
arr=(5 3 8 4 2 7 1 6)
echo "Original: ${arr[@]}"
echo "Bubble sort: $(bubble_sort "${arr[@]}")"
echo "Bubble sort optimized: $(bubble_sort_optimized "${arr[@]}")"Find common elements between two arrays.
- Method:
for item1 in "{arr1[@]}"; do for item2 in "{arr2[@]}"; do ... - Alternative: Use associative array
- Time: O(n*m)
- Unique: Remove duplicates
#!/bin/bash
# Intersection of arrays
intersection() {
local arr1=("${!1}")
local arr2=("${!2}")
local result=()
for item1 in "${arr1[@]}"; do
for item2 in "${arr2[@]}"; do
if [ "$item1" = "$item2" ]; then
local found=0
for res in "${result[@]}"; do
if [ "$res" = "$item1" ]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
result+=("$item1")
fi
break
fi
done
done
echo "${result[@]}"
}
arr1=(apple banana orange grape kiwi)
arr2=(banana kiwi mango grape)
echo "Intersection: $(intersection arr1 arr2)"
ints1=(1 2 3 4 5)
ints2=(4 5 6 7 8)
echo "Intersection (ints): $(intersection ints1 ints2)"Combine arrays with unique elements.
- Method:
result=("{arr1[@]}"); for item in "{arr2[@]}"; do ... - Alternative:
printf '%s\n' "{arr1[@]}" "{arr2[@]}" | sort -u - Time: O(n*m) or O(n log n) with sort
- Preserve order: Manual method
#!/bin/bash
# Union of arrays
union() {
local arr1=("${!1}")
local arr2=("${!2}")
local result=("${arr1[@]}")
for item in "${arr2[@]}"; do
local found=0
for res in "${result[@]}"; do
if [ "$res" = "$item" ]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
result+=("$item")
fi
done
echo "${result[@]}"
}
arr1=(apple banana orange)
arr2=(orange grape kiwi)
echo "Union: $(union arr1 arr2)"
ints1=(1 2 3 4)
ints2=(4 5 6 7)
echo "Union (ints): $(union ints1 ints2)"Find elements in first array not in second.
- Method:
for item in "{arr1[@]}"; do found=0; for item2 in "{arr2[@]}"; do ... - Symmetric: Both directions
- Time: O(n*m)
- Returns: Difference
#!/bin/bash
# Difference of arrays
difference() {
local arr1=("${!1}")
local arr2=("${!2}")
local result=()
for item in "${arr1[@]}"; do
local found=0
for item2 in "${arr2[@]}"; do
if [ "$item" = "$item2" ]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
result+=("$item")
fi
done
echo "${result[@]}"
}
symmetric_difference() {
local arr1=("${!1}")
local arr2=("${!2}")
local diff1=($(difference arr1 arr2))
local diff2=($(difference arr2 arr1))
echo "${diff1[@]} ${diff2[@]}"
}
arr1=(apple banana orange grape)
arr2=(banana kiwi grape)
echo "Difference: $(difference arr1 arr2)"
echo "Symmetric difference: $(symmetric_difference arr1 arr2)"
ints1=(1 2 3 4 5)
ints2=(4 5 6 7 8)
echo "Difference (ints): $(difference ints1 ints2)"Group data by a property using associative arrays.
- Method:
declare -A groups; for item in ...; do key="{item#*:}"; groups[$key]="{groups[$key]} $item"; done - Use case: Data aggregation
- Time: O(n)
- Output: Grouped data
#!/bin/bash
# Group by property
declare -A people
people[1]="Alice:25:NYC"
people[2]="Bob:30:LA"
people[3]="Charlie:25:NYC"
people[4]="David:35:Chicago"
people[5]="Eve:30:LA"
group_by_age() {
local groups=()
for key in "${!people[@]}"; do
local person=${people[$key]}
IFS=':' read -r name age city <<< "$person"
local key="$age"
groups+=("$key:$name")
done
echo "${groups[@]}"
}
group_by_city() {
local groups=()
for key in "${!people[@]}"; do
local person=${people[$key]}
IFS=':' read -r name age city <<< "$person"
local key="$city"
groups+=("$key:$name")
done
echo "${groups[@]}"
}
echo "Group by age:"
for group in $(group_by_age); do
IFS=':' read -r age name <<< "$group"
echo "Age $age: $name"
done
echo "Group by city:"
for group in $(group_by_city); do
IFS=':' read -r city name <<< "$group"
echo "City $city: $name"
doneCreate a deep copy of an array or associative array.
- Array:
clone=("{original[@]}") - Associative:
for key in "{!original[@]}"; do clone[$key]="{original[$key]}"; done - Nested: Recursive copy
- Time: O(n)
#!/bin/bash
# Deep clone (using eval)
deep_clone() {
local var_name=$1
local clone_name="${var_name}_clone"
eval "$clone_name="${$var_name[@]}""
eval "declare -a $clone_name=(${$var_name[@]})"
}
# Example
original=(1 2 3)
deep_clone original
eval "cloned=(${original_clone[@]})"
echo "Original: ${original[@]}"
echo "Cloned: ${cloned[@]}"
# Deep clone for associative arrays
deep_clone_assoc() {
local var_name=$1
local clone_name="${var_name}_clone"
eval "declare -A $clone_name"
eval "for key in "${!$var_name[@]}"; do $clone_name[$key]="${$var_name[$key]}"; done"
}
declare -A map
map[name]="Alice"
map[age]=25
deep_clone_assoc map
eval "declare -A map_clone"
eval "for key in "${!map_clone[@]}"; do echo "$key => ${map_clone[$key]}"; done"Perform immutable updates by copying and modifying.
- Method:
clone=("{original[@]}"); clone[$index]="$value" - Nested: Copy and update nested
- Return: New array
- Use case: State management
#!/bin/bash
# Immutable update
update_immutable() {
local var_name=$1
local path=$2
local value=$3
eval "local clone=(${$var_name[@]})"
eval "clone[$path]="$value""
echo "${clone[@]}"
}
# Nested update (using associative arrays)
update_nested() {
local var_name=$1
local key=$2
local value=$3
eval "declare -A clone"
eval "for k in "${!$var_name[@]}"; do clone[$k]="${$var_name[$k]}"; done"
eval "clone[$key]="$value""
echo "${clone[@]}"
}
state=(name Alice age 25)
new_state=($(update_immutable state 1 Bob))
echo "Original: ${state[@]}"
echo "Updated: ${new_state[@]}"
declare -A person
person[name]="Alice"
person[age]=25
person[city]="NYC"
# Update age
eval "new_person=($(update_nested person age 26))"
echo "Original: ${person[@]}"
echo "Updated: ${new_person[@]}"Implement pipe for left-to-right function composition.
- Method:
pipe() { value=$1; shift; for func in "$@"; do value=$($func "$value"); done; echo "$value"; } - Direction: Left to right
- Use case: Function chaining
- Implementation: Loop functions
#!/bin/bash
# Pipe function
pipe() {
local value=$1
shift
for func in "$@"; do
value=$($func "$value")
done
echo "$value"
}
double() { echo $(( $1 * 2 )); }
add_ten() { echo $(( $1 + 10 )); }
square() { echo $(( $1 * $1 )); }
result=$(pipe 5 double add_ten square)
echo "Pipe: $result"
# Using pipeline with functions
double_pipe() { while read -r x; do echo $((x * 2)); done; }
add_ten_pipe() { while read -r x; do echo $((x + 10)); done; }
square_pipe() { while read -r x; do echo $((x * x)); done; }
result2=$(echo 5 | double_pipe | add_ten_pipe | square_pipe)
echo "Pipeline: $result2"Implement compose for right‑to‑left function composition.
- Method:
compose() { result=$1; shift; for ((i=${#@}-1; i>=0; i--)); do result=${@:$i:1} "$result"); done; echo "$result"; } - Direction: Right to left
- Use case: Function composition
- Alternative:
compose_alt
#!/bin/bash
# Compose function
compose() {
local funcs=("$@")
local result=$1
shift
for ((i=${#funcs[@]}-1; i>=0; i--)); do
result=$(${funcs[$i]} "$result")
done
echo "$result"
}
double() { echo $(( $1 * 2 )); }
add_ten() { echo $(( $1 + 10 )); }
square() { echo $(( $1 * $1 )); }
result=$(compose 5 double add_ten square)
echo "Composed: $result"
# Alternative compose
compose_alt() {
local funcs=("$@")
local value=$1
shift
for func in "${funcs[@]}"; do
value=$($func "$value")
done
echo "$value"
}
result2=$(compose_alt 5 square add_ten double)
echo "Composed alt: $result2"Implement memoization using associative arrays.
- Cache:
declare -A cache - Check:
[[ -v cache[$key] ]] - Store:
cache[$key]="$result" - File cache: Use temporary files
#!/bin/bash
# Memoization
declare -A memo_cache
memoize() {
local func=$1
local arg=$2
local cache_key="${func}_$arg"
if [ -n "${memo_cache[$cache_key]}" ]; then
echo "${memo_cache[$cache_key]}"
return
fi
local result=$($func $arg)
memo_cache[$cache_key]=$result
echo $result
}
fib() {
if [ $1 -le 1 ]; then
echo $1
else
local a=$(memoize fib $(( $1 - 1 )))
local b=$(memoize fib $(( $1 - 2 )))
echo $((a + b))
fi
}
echo "Fibonacci(10): $(fib 10)"
echo "Fibonacci(10) again: $(fib 10)"
# Using file-based cache
memoize_file() {
local func=$1
local arg=$2
local cache_dir="/tmp/memo_cache"
mkdir -p "$cache_dir"
local cache_file="$cache_dir/${func}_$arg"
if [ -f "$cache_file" ]; then
cat "$cache_file"
return
fi
local result=$($func $arg)
echo "$result" > "$cache_file"
echo $result
}Implement once function that ensures a function is called only once.
- Flag:
called=0 - Check:
if [ $called -eq 0 ]; then ... - Result: Cache result
- Reset: Reset flag
#!/bin/bash
# Once function
declare -A once_called
declare -A once_results
once() {
local func=$1
local arg=$2
local key="${func}_$arg"
if [ -n "${once_called[$key]}" ]; then
echo "${once_results[$key]}"
return
fi
once_called[$key]=1
local result=$($func $arg)
once_results[$key]=$result
echo $result
}
initialize() {
echo "Initialized with $1"
echo $(( $1 * 2 ))
}
echo "First: $(once initialize 10)"
echo "Second: $(once initialize 20)"
# Once with reset
once_with_reset() {
local func=$1
local arg=$2
local key="${func}_$arg"
if [ -n "${once_called[$key]}" ]; then
echo "${once_results[$key]}"
return
fi
once_called[$key]=1
local result=$($func $arg)
once_results[$key]=$result
echo $result
}
reset_once() {
local func=$1
local arg=$2
local key="${func}_$arg"
unset once_called[$key]
unset once_results[$key]
}
init() {
echo "Initialized with $1"
echo $(( $1 * 2 ))
}
echo "First: $(once_with_reset init 10)"
reset_once init 10
echo "After reset: $(once_with_reset init 20)"Implement debounce with leading edge using timers.
- Last call: Track time
- Execute: If enough time passed
- Timeout: Schedule delayed execution
- Use case: Rate limiting
#!/bin/bash
# Debounce with leading edge
declare -A debounce_last_call
declare -A debounce_timeout
debounce_leading() {
local func=$1
local delay=$2
local arg=$3
local key="${func}_$arg"
local now=$(date +%s)
if [ -z "${debounce_last_call[$key]}" ] || [ $((now - debounce_last_call[$key])) -ge $delay ]; then
debounce_last_call[$key]=$now
$func $arg
else
if [ -z "${debounce_timeout[$key]}" ]; then
(
sleep $((delay - (now - debounce_last_call[$key])))
debounce_last_call[$key]=$(date +%s)
$func $arg
unset debounce_timeout[$key]
) &
debounce_timeout[$key]=1
fi
fi
}
process() {
echo "Processing: $1"
}
debounce_leading process 2 "value1"
debounce_leading process 2 "value2"
sleep 3
debounce_leading process 2 "value3"Implement throttle with leading edge by checking time since last call.
- Last call: Track time
- Execute: If enough time passed
- Trailing: Schedule pending execution
- Use case: Scroll events
#!/bin/bash
# Throttle with leading edge
declare -A throttle_last_call
throttle_leading() {
local func=$1
local delay=$2
local arg=$3
local key="${func}_$arg"
local now=$(date +%s)
if [ -z "${throttle_last_call[$key]}" ] || [ $((now - throttle_last_call[$key])) -ge $delay ]; then
throttle_last_call[$key]=$now
$func $arg
fi
}
process() {
echo "Processing: $1"
}
throttle_leading process 2 "value1"
throttle_leading process 2 "value2"
sleep 3
throttle_leading process 2 "value3"
# Throttle with trailing
declare -A throttle_pending
declare -A throttle_timer
throttle_trailing() {
local func=$1
local delay=$2
local arg=$3
local key="${func}_$arg"
local now=$(date +%s)
if [ -z "${throttle_last_call[$key]}" ] || [ $((now - throttle_last_call[$key])) -ge $delay ]; then
throttle_last_call[$key]=$now
$func $arg
else
throttle_pending[$key]=$arg
if [ -z "${throttle_timer[$key]}" ]; then
local remaining=$((delay - (now - throttle_last_call[$key])))
(
sleep $remaining
throttle_last_call[$key]=$(date +%s)
if [ -n "${throttle_pending[$key]}" ]; then
$func ${throttle_pending[$key]}
unset throttle_pending[$key]
fi
unset throttle_timer[$key]
) &
throttle_timer[$key]=1
fi
fi
}Implement deep equality for arrays and associative arrays.
- Method:
deep_equal() { local -n arr1=$1; local -n arr2=$2; ... } - Length: Must be equal
- Elements: Compare each
- Types: Check array types
#!/bin/bash
# Deep equal
deep_equal() {
if [ "$1" = "$2" ]; then
return 0
fi
local type1=$(declare -p "$1" 2>/dev/null)
local type2=$(declare -p "$2" 2>/dev/null)
if [ "$type1" != "$type2" ]; then
return 1
fi
eval "local arr1=(${$1[@]})"
eval "local arr2=(${$2[@]})"
if [ ${#arr1[@]} -ne ${#arr2[@]} ]; then
return 1
fi
for ((i=0; i<${#arr1[@]}; i++)); do
if [ "${arr1[$i]}" != "${arr2[$i]}" ]; then
return 1
fi
done
return 0
}
arr1=(1 2 3)
arr2=(1 2 3)
arr3=(1 2 4)
if deep_equal arr1 arr2; then
echo "arr1 == arr2"
else
echo "arr1 != arr2"
fi
if deep_equal arr1 arr3; then
echo "arr1 == arr3"
else
echo "arr1 != arr3"
fi
# Deep equal for associative arrays
deep_equal_assoc() {
local -n arr1=$1
local -n arr2=$2
if [ ${#arr1[@]} -ne ${#arr2[@]} ]; then
return 1
fi
for key in "${!arr1[@]}"; do
if [ "${arr1[$key]}" != "${arr2[$key]}" ]; then
return 1
fi
done
return 0
}
declare -A map1 map2 map3
map1[name]="Alice"; map1[age]=25
map2[name]="Alice"; map2[age]=25
map3[name]="Alice"; map3[age]=26
if deep_equal_assoc map1 map2; then
echo "map1 == map2"
fi
if deep_equal_assoc map1 map3; then
echo "map1 == map3"
fiImplement observable pattern with subscribers and notification.
- Subscribers: Associative array
- Subscribe:
subscribers[$id]="$callback" - Notify:
for id in "{!subscribers[@]}"; do {subscribers[$id]} "$data"; done - Stateful: Track state
#!/bin/bash
# Observable pattern
declare -A subscribers
observable() {
local action=$1
shift
case $action in
subscribe)
local id=$1
local callback=$2
subscribers[$id]=$callback
;;
unsubscribe)
local id=$1
unset subscribers[$id]
;;
notify)
local data=$1
for id in "${!subscribers[@]}"; do
${subscribers[$id]} "$data"
done
;;
esac
}
# Stateful observable
declare -A stateful_subscribers
stateful_observable() {
local action=$1
shift
case $action in
subscribe)
local id=$1
local callback=$2
stateful_subscribers[$id]=$callback
;;
set_state)
local data=$1
state=$data
for id in "${!stateful_subscribers[@]}"; do
${stateful_subscribers[$id]} "$data"
done
;;
get_state)
echo "$state"
;;
esac
}
# Usage
observable subscribe "observer1" "echo 'Observer1: $1'"
observable subscribe "observer2" "echo 'Observer2: $1'"
observable notify "Hello, World!"
observable unsubscribe "observer1"
observable notify "Hello again!"
stateful_observable set_state "Initial"
stateful_observable subscribe "state_obs" "echo 'State changed to: $1'"
stateful_observable set_state "New State"Implement singleton pattern using associative arrays or variables.
- Instance:
singleton_data[$class]="initialized" - Check:
if [ -z "{singleton_data[$class]}" ]; then ... - Return: Existing instance
- Factory: Create on demand
#!/bin/bash
# Singleton pattern
declare -A singleton_data
get_singleton() {
local class=$1
if [ -z "${singleton_data[$class]}" ]; then
singleton_data[$class]="initialized"
echo "Creating singleton for $class"
fi
echo "${singleton_data[$class]}"
}
# Singleton with data
declare -A singleton_instances
singleton() {
local class=$1
if [ -z "${singleton_instances[$class]}" ]; then
singleton_instances[$class]="instance"
echo "Singleton $class created"
fi
echo "${singleton_instances[$class]}"
}
# Singleton factory
singleton_factory() {
local instance_var="singleton_${1}_instance"
if [ -z "${!instance_var}" ]; then
eval "$instance_var='created'"
echo "Singleton created: $1"
fi
eval "echo $$instance_var"
}
# Usage
get_singleton "config"
get_singleton "config"
singleton "database"
singleton "database"
singleton_factory "logger"
singleton_factory "logger"
# Singleton with data storage
declare -A singleton_store
singleton_store_init() {
local name=$1
if [ -z "${singleton_store[$name]}" ]; then
singleton_store[$name]="{}"
echo "Store $name initialized"
fi
echo "${singleton_store[$name]}"
}
singleton_store_set() {
local name=$1
local key=$2
local value=$3
local data=$(singleton_store_init "$name")
# Simple key-value storage
eval "declare -A temp"
eval "temp=($data)"
temp[$key]=$value
local new_data=""
for k in "${!temp[@]}"; do
new_data="$new_data $k=${temp[$k]}"
done
singleton_store[$name]="$new_data"
}
singleton_store_get() {
local name=$1
local key=$2
local data=$(singleton_store_init "$name")
eval "declare -A temp"
eval "temp=($data)"
echo "${temp[$key]:-}"
}Implement factory pattern for creating objects with different types.
- Factory:
create_user() { case $type in ... } - Types:
admin,guest,regular - Parameters:
name,permissions - Return: User string
#!/bin/bash
# Factory pattern
create_user() {
local type=$1
local name=$2
case $type in
admin)
echo "admin:$name"
;;
guest)
echo "guest:$name"
;;
*)
echo "regular:$name"
;;
esac
}
# User factory with validation
user_factory() {
local type=$1
local name=$2
if [ -z "$name" ]; then
echo "Error: Name is required" >&2
return 1
fi
create_user "$type" "$name"
}
# Factory with permissions
create_user_with_permissions() {
local type=$1
local name=$2
local permissions=$3
case $type in
admin)
echo "admin:$name:$permissions"
;;
guest)
echo "guest:$name:"
;;
*)
echo "regular:$name:"
;;
esac
}
# Usage
user1=$(create_user admin Alice)
user2=$(create_user guest Bob)
user3=$(create_user regular Charlie)
echo "$user1"
echo "$user2"
echo "$user3"
user4=$(user_factory admin "Alice")
user5=$(user_factory guest "Bob")
echo "$user4"
echo "$user5"Implement strategy pattern with interchangeable strategies.
- Strategies: Functions
- Context:
payment_context() { case $strategy in ... } - Execute: Call selected strategy
- Decorator: Add discount
#!/bin/bash
# Strategy pattern
credit_card_payment() {
echo "Paid $1 with Credit Card"
}
paypal_payment() {
echo "Paid $1 with PayPal"
}
crypto_payment() {
echo "Paid $1 with Crypto"
}
payment_context() {
local strategy=$1
local amount=$2
case $strategy in
credit)
credit_card_payment "$amount"
;;
paypal)
paypal_payment "$amount"
;;
crypto)
crypto_payment "$amount"
;;
*)
echo "Unknown strategy"
;;
esac
}
# Strategy with discount
discount_decorator() {
local strategy=$1
local discount=$2
local amount=$3
local discounted=$(echo "$amount * (1 - $discount)" | bc)
echo "Applied discount of $(($discount * 100))%"
payment_context "$strategy" "$discounted"
}
# Usage
payment_context credit 100
payment_context paypal 50
payment_context crypto 75
discount_decorator paypal 0.1 100Implement observer pattern with subject and observers.
- Subject:
subject() { case $action in attach|detach|notify) ... } - Observers: Associative array
- Attach: Add observer
- Notify: Call all observers
#!/bin/bash
# Observer pattern
declare -A observers
subject() {
local action=$1
shift
case $action in
attach)
local id=$1
local callback=$2
observers[$id]=$callback
;;
detach)
local id=$1
unset observers[$id]
;;
notify)
local data=$1
for id in "${!observers[@]}"; do
${observers[$id]} "$data"
done
;;
esac
}
# Concrete subject with state
declare -A stateful_observers
subject_state=""
stateful_subject() {
local action=$1
shift
case $action in
attach)
local id=$1
local callback=$2
stateful_observers[$id]=$callback
;;
detach)
local id=$1
unset stateful_observers[$id]
;;
set_state)
subject_state=$1
for id in "${!stateful_observers[@]}"; do
${stateful_observers[$id]} "$subject_state"
done
;;
get_state)
echo "$subject_state"
;;
esac
}
# Usage
subject attach "obs1" "echo 'Observer1: $1'"
subject attach "obs2" "echo 'Observer2: $1'"
subject notify "Hello, World!"
subject detach "obs1"
subject notify "Hello again!"
stateful_subject attach "state_obs" "echo 'State changed to: $1'"
stateful_subject set_state "Initial State"
stateful_subject set_state "New State"Implement decorator pattern for adding features.
- Component:
basic_coffee() { echo "Coffee"; echo "5.0"; } - Decorators:
milk_decorator() { ... } - Chaining:
apply_decorators() { for decorator in "$@"; do ... } - Composition: Nested calls
#!/bin/bash
# Decorator pattern
basic_coffee() {
echo "Coffee"
echo "5.0"
}
milk_decorator() {
local desc=$1
local cost=$2
echo "$desc, Milk"
echo "$cost + 2.0" | bc
}
sugar_decorator() {
local desc=$1
local cost=$2
echo "$desc, Sugar"
echo "$cost + 1.0" | bc
}
caramel_decorator() {
local desc=$1
local cost=$2
echo "$desc, Caramel"
echo "$cost + 2.5" | bc
}
whipped_cream_decorator() {
local desc=$1
local cost=$2
echo "$desc, Whipped Cream"
echo "$cost + 1.5" | bc
}
apply_decorators() {
local desc="Coffee"
local cost="5.0"
for decorator in "$@"; do
case $decorator in
milk)
local result=$(milk_decorator "$desc" "$cost")
desc=$(echo "$result" | head -1)
cost=$(echo "$result" | tail -1)
;;
sugar)
local result=$(sugar_decorator "$desc" "$cost")
desc=$(echo "$result" | head -1)
cost=$(echo "$result" | tail -1)
;;
caramel)
local result=$(caramel_decorator "$desc" "$cost")
desc=$(echo "$result" | head -1)
cost=$(echo "$result" | tail -1)
;;
whipped)
local result=$(whipped_cream_decorator "$desc" "$cost")
desc=$(echo "$result" | head -1)
cost=$(echo "$result" | tail -1)
;;
esac
done
echo "$desc ($cost)"
}
# Usage
basic_coffee
milk_decorator "Coffee" "5.0"
sugar_decorator "Coffee" "5.0"
apply_decorators milk sugar caramel whippedImplement command pattern with execute, undo, and redo.
- Command:
add_command() { counter=$((counter + value)); command_history+=("add:$value"); } - Undo:
undo() { last={command_history[-1]}; ... } - Redo: Re-execute commands
- History: Store commands
#!/bin/bash
# Command pattern
declare -a command_history
declare -a command_results
add_command() {
local value=$1
counter=$((counter + value))
command_history+=("add:$value")
}
subtract_command() {
local value=$1
counter=$((counter - value))
command_history+=("subtract:$value")
}
undo() {
if [ ${#command_history[@]} -gt 0 ]; then
local last=${command_history[-1]}
unset 'command_history[-1]'
local cmd=${last%:*}
local value=${last#*:}
case $cmd in
add) counter=$((counter - value));;
subtract) counter=$((counter + value));;
esac
fi
}
redo() {
# Re-execute commands from history
local cmd=${command_history[-1]}
case $cmd in
add:*) add_command ${cmd#*:};;
subtract:*) subtract_command ${cmd#*:};;
esac
}
macro_command() {
for cmd in "$@"; do
case $cmd in
add:*) add_command ${cmd#*:};;
subtract:*) subtract_command ${cmd#*:};;
esac
done
}
# Usage
counter=0
add_command 5
echo "Counter: $counter"
subtract_command 3
echo "Counter: $counter"
undo
echo "After undo: $counter"
macro_command "add:5" "add:5" "subtract:3"
echo "After macro: $counter"Implement memento pattern for state capture and restoration.
- Save:
save_state() { mementos+=("$1"); } - Restore:
restore_state() { local state={mementos[-1]}; unset 'mementos[-1]'; echo "$state"; } - Originator: Holds state
- Caretaker: Manages mementos
#!/bin/bash
# Memento pattern
declare -a mementos
declare -a memento_states
save_state() {
local state=$1
mementos+=("$state")
}
restore_state() {
if [ ${#mementos[@]} -gt 0 ]; then
local state=${mementos[-1]}
unset 'mementos[-1]'
echo "$state"
fi
}
# Originator
originator_state=""
set_originator_state() {
originator_state=$1
save_state "$originator_state"
}
get_originator_state() {
echo "$originator_state"
}
# Usage
set_originator_state 0
set_originator_state 1
set_originator_state 2
set_originator_state 3
echo "Current: $(get_originator_state)"
restored=$(restore_state)
echo "After undo: $restored"
restored=$(restore_state)
echo "After redo: $restored"
# Memento with caretaker
declare -a caretaker_mementos
declare -a caretaker_index
caretaker_save() {
local state=$1
caretaker_mementos+=("$state")
}
caretaker_undo() {
if [ ${#caretaker_mementos[@]} -gt 0 ]; then
local state=${caretaker_mementos[-1]}
unset 'caretaker_mementos[-1]'
echo "$state"
fi
}
caretaker_redo() {
# Re-apply mementos
if [ ${#caretaker_mementos[@]} -gt 0 ]; then
echo "${caretaker_mementos[-1]}"
fi
}Implement mediator pattern for centralized communication.
- Mediator:
mediator() { case $action in register|send) ... } - Colleagues: Associate array of callbacks
- Register: Add colleague
- Send: Route messages
#!/bin/bash
# Mediator pattern
declare -A mediator_colleagues
mediator() {
local action=$1
shift
case $action in
register)
local name=$1
local callback=$2
mediator_colleagues[$name]=$callback
;;
send)
local message=$1
local sender=$2
for name in "${!mediator_colleagues[@]}"; do
if [ "$name" != "$sender" ]; then
${mediator_colleagues[$name]} "$message"
fi
done
;;
esac
}
# Colleague
colleague() {
local name=$1
local receive_func=$2
echo "$name"
}
# Usage
mediator register "Alice" "echo 'Alice received: $1'"
mediator register "Bob" "echo 'Bob received: $1'"
mediator register "Charlie" "echo 'Charlie received: $1'"
mediator send "Hello everyone!" "Alice"
# Stateful colleague
declare -A colleague_states
stateful_colleague() {
local name=$1
local state=$2
colleague_states[$name]=$state
mediator register "$name" "echo '$name (state $state) received: $1'"
}
stateful_colleague "Alice" 0
stateful_colleague "Bob" 1
mediator send "Custom message" "Alice"Implement chain of responsibility with linked handlers.
- Handlers:
auth,logger,validator - Chain:
chain_handlers[$handler]="$next" - Process:
handler handle "$handler" "$request" - Stop: On failure
#!/bin/bash
# Chain of Responsibility
declare -A chain_handlers
handler() {
local action=$1
shift
case $action in
set_next)
local handler=$1
local next=$2
chain_handlers[$handler]=$next
;;
handle)
local handler=$1
local request=$2
case $handler in
auth)
if echo "$request" | grep -q "token"; then
echo "Authentication passed"
local next=${chain_handlers[$handler]}
if [ -n "$next" ]; then
handler handle "$next" "$request"
fi
else
echo "Authentication failed"
fi
;;
logger)
local url=$(echo "$request" | grep -o "url=[^&]*" | cut -d= -f2)
echo "Logging request: ${url:-unknown}"
local next=${chain_handlers[$handler]}
if [ -n "$next" ]; then
handler handle "$next" "$request"
fi
;;
validator)
if echo "$request" | grep -q "data"; then
echo "Validation passed"
local next=${chain_handlers[$handler]}
if [ -n "$next" ]; then
handler handle "$next" "$request"
fi
else
echo "Validation failed"
fi
;;
ratelimit)
echo "Rate limit passed"
local next=${chain_handlers[$handler]}
if [ -n "$next" ]; then
handler handle "$next" "$request"
fi
;;
esac
;;
esac
}
# Setup chain
handler set_next "auth" "logger"
handler set_next "logger" "validator"
handler set_next "validator" "ratelimit"
# Usage
echo "Processing valid request:"
handler handle "auth" "token=valid&url=/api&data=payload"
echo "Processing invalid request:"
handler handle "auth" "url=/public"Implement state pattern with context and state transitions.
- Context:
current_state="ready" - States:
handle_state() { case $current_state in ready|processing|completed) ... } - Transition: Change state
- Data: Track state data
#!/bin/bash
# State pattern
current_state="ready"
handle_state() {
case $current_state in
ready)
echo "Ready: Waiting for input"
current_state="processing"
;;
processing)
echo "Processing: Working on task"
current_state="completed"
;;
completed)
echo "Completed: Task finished"
current_state="ready"
;;
error)
echo "Error: Something went wrong"
current_state="ready"
;;
esac
}
# State with data
declare -A state_data
stateful_context() {
local state=$1
case $state in
ready)
echo "Ready: Waiting for input"
state_data["last_state"]="ready"
current_state="processing"
;;
processing)
echo "Processing: Working on task"
state_data["last_state"]="processing"
current_state="completed"
;;
completed)
echo "Completed: Task finished"
state_data["last_state"]="completed"
current_state="ready"
;;
error)
echo "Error: Something went wrong"
state_data["last_state"]="error"
current_state="ready"
;;
esac
}
# Usage
for i in {1..5}; do
echo "Step $i:"
handle_state
echo "State data: ${state_data["last_state"]:-none}"
doneImplement proxy pattern for access control and lazy initialization.
- Real subject:
real_subject() { echo "RealSubject: Handling request"; } - Proxy:
proxy() { if [ -z "$cached_subject" ]; then ... } - Logging:
logging_proxy() { echo "Logging"; real_subject; } - Auth:
auth_proxy() { [ "$user" = "admin" ] && real_subject; }
#!/bin/bash
# Proxy pattern
real_subject() {
echo "RealSubject: Handling request"
}
proxy() {
if [ -z "$cached_subject" ]; then
echo "Proxy: Creating real subject"
cached_subject="created"
fi
echo "Proxy: Using cached real subject"
real_subject
}
logging_proxy() {
echo "Logging: Request started"
real_subject
echo "Logging: Request completed"
}
auth_proxy() {
local user=$1
if [ "$user" = "admin" ]; then
echo "Auth: Access granted"
real_subject
else
echo "Auth: Access denied"
echo "Unauthorized"
fi
}
# Usage
echo "$(proxy)"
echo "$(proxy)"
echo "$(logging_proxy)"
echo "$(auth_proxy admin)"
echo "$(auth_proxy guest)"
# Proxy with caching
declare -A proxy_cache
caching_proxy() {
local key=$1
if [ -n "${proxy_cache[$key]}" ]; then
echo "Cache hit: ${proxy_cache[$key]}"
else
local result=$(real_subject)
proxy_cache[$key]=$result
echo "Cache miss: $result"
fi
}Implement flyweight pattern for sharing objects.
- Flyweight:
flyweight() { echo "flyweight_$1"; } - Factory:
flyweight_cache[$shared_state]="flyweight_$shared_state" - Operation:
flyweight_operation() { echo "Shared: $flyweight, Unique: $unique_state"; } - Share: Reuse instances
#!/bin/bash
# Flyweight pattern
declare -A flyweight_cache
flyweight() {
local shared_state=$1
if [ -z "${flyweight_cache[$shared_state]}" ]; then
flyweight_cache[$shared_state]="flyweight_$shared_state"
fi
echo "${flyweight_cache[$shared_state]}"
}
flyweight_operation() {
local flyweight=$1
local unique_state=$2
echo "Shared: $flyweight, Unique: $unique_state"
}
# Usage
fw1=$(flyweight "state1")
fw2=$(flyweight "state1")
fw3=$(flyweight "state2")
echo "fw1 and fw2 are same: $([ "$fw1" = "$fw2" ] && echo "true" || echo "false")"
echo "fw1 and fw3 are same: $([ "$fw1" = "$fw3" ] && echo "true" || echo "false")"
flyweight_operation "$fw1" "unique1"
flyweight_operation "$fw2" "unique2"
flyweight_operation "$fw3" "unique3"
echo "Number of flyweights: ${#flyweight_cache[@]}"Implement bridge pattern for separating abstraction from implementation.
- Implementation:
impl_a() { echo "ConcreteImplementationA: Operation"; } - Abstraction:
abstraction() { case $impl in A|B) ... } - Extended:
extended_abstraction() { ... } - Alternative:
alternative_abstraction() { ... }
#!/bin/bash
# Bridge pattern
impl_a() {
echo "ConcreteImplementationA: Operation"
}
impl_b() {
echo "ConcreteImplementationB: Operation"
}
abstraction() {
local impl=$1
case $impl in
A) echo "Abstraction: Additional logic - $(impl_a)";;
B) echo "Abstraction: Additional logic - $(impl_b)";;
esac
}
extended_abstraction() {
local impl=$1
case $impl in
A) echo "Extended: More logic - $(impl_a)";;
B) echo "Extended: More logic - $(impl_b)";;
esac
}
alternative_abstraction() {
local impl=$1
case $impl in
A) echo "Alternative: Different logic - $(impl_a)";;
B) echo "Alternative: Different logic - $(impl_b)";;
esac
}
# Usage
abstraction A
abstraction B
extended_abstraction A
extended_abstraction B
alternative_abstraction A
alternative_abstraction BImplement adapter pattern for converting interfaces.
- Target:
target() { echo "Target: Request"; } - Adaptee:
adaptee() { echo "Adaptee: Specific Request"; } - Adapter:
adapter() { adaptee; } - Logging:
logging_adapter() { echo "Logging"; adaptee; }
#!/bin/bash
# Adapter pattern
target() {
echo "Target: Request"
}
adaptee() {
echo "Adaptee: Specific Request"
}
adapter() {
adaptee
}
logging_adapter() {
echo "Adapter: Logging request"
adaptee
}
# Usage
echo "$(target)"
echo "$(adapter)"
echo "$(logging_adapter)"
# Adapter with conversion
numeric_adapter() {
local input=$1
echo "$((input * 2))"
}
string_adapter() {
local input=$1
echo "Converted: $input"
}
adapter_with_conversion() {
local type=$1
local data=$2
case $type in
numeric) numeric_adapter "$data";;
string) string_adapter "$data";;
esac
}Implement facade pattern for simplifying complex subsystems.
- Subsystems:
subsystem_a() { echo "SubsystemA: Operation"; } - Facade:
facade() { case $type in simple|complex) ... } - Simplified:
simplified_facade() { facade simple; } - Config:
facade_config() { case $config in minimal|standard|full) ... }
#!/bin/bash
# Facade pattern
subsystem_a() {
echo "SubsystemA: Operation"
}
subsystem_b() {
echo "SubsystemB: Operation"
}
subsystem_c() {
echo "SubsystemC: Operation"
}
facade() {
local type=$1
case $type in
simple)
subsystem_a
;;
complex)
subsystem_a
subsystem_b
subsystem_c
;;
esac
}
simplified_facade() {
facade simple
}
# Usage
echo "Simple operation:"
facade simple
echo "Complex operation:"
facade complex
echo "Simplified facade:"
simplified_facade
# Facade with configuration
facade_config() {
local config=$1
case $config in
minimal)
subsystem_a
;;
standard)
subsystem_a
subsystem_b
;;
full)
subsystem_a
subsystem_b
subsystem_c
;;
esac
}Implement composite pattern for tree structures.
- Leaf:
leaf() { echo "Leaf $1: Operation"; } - Composite:
composite() { echo "Composite $1: Operation"; for child in "{@:2}"; do eval "$child"; done; } - Add:
add_child() { ... } - Count:
count_leaves() { ... }
#!/bin/bash
# Composite pattern
leaf() {
local name=$1
echo "Leaf $name: Operation"
}
composite() {
local name=$1
shift
local children=("$@")
echo "Composite $name: Operation"
for child in "${children[@]}"; do
eval "$child"
done
}
# Usage
leaf1="leaf A"
leaf2="leaf B"
leaf3="leaf C"
leaf4="leaf D"
composite1="composite Comp1 $leaf1 $leaf2"
composite2="composite Comp2 $leaf3 $composite1"
root="composite Root $leaf4 $composite2"
eval "$root"
# Composite with count
count_leaves() {
local name=$1
shift
local children=("$@")
local count=0
for child in "${children[@]}"; do
if [[ "$child" == leaf* ]]; then
count=$((count + 1))
else
local sub_count=$(eval "$child; echo $?")
count=$((count + sub_count))
fi
done
echo $count
}Implement visitor pattern for adding operations to objects.
- Visitor:
concrete_visitor() { case $element in A*|B*) ... } - Elements:
element_a() { echo "ElementA: $data"; } - Accept:
concrete_visitor "$element" - Counting:
counting_visitor() { ... }
#!/bin/bash
# Visitor pattern
element_a() {
local data=$1
echo "ElementA: $data"
}
element_b() {
local data=$1
echo "ElementB: $data"
}
concrete_visitor() {
local element=$1
case $element in
A*) element_a "${element#A:}";;
B*) element_b "${element#B:}";;
esac
}
counting_visitor() {
local element=$1
local count_a=$2
local count_b=$3
case $element in
A*)
count_a=$((count_a + 1))
echo "Visiting ElementA ($count_a): ${element#A:}"
;;
B*)
count_b=$((count_b + 1))
echo "Visiting ElementB ($count_b): ${element#B:}"
;;
esac
echo "$count_a:$count_b"
}
extended_visitor() {
local element=$1
case $element in
A*) echo "Extended: ${element#A:} (A)";;
B*) echo "Extended: ${element#B:} (B)";;
esac
}
# Usage
elements=("A:Hello" "B:World" "A:Shell" "B:Visitor")
echo "Using standard visitor:"
for el in "${elements[@]}"; do
concrete_visitor "$el"
done
echo "Using counting visitor:"
count_a=0
count_b=0
for el in "${elements[@]}"; do
result=$(counting_visitor "$el" "$count_a" "$count_b")
count_a=$(echo "$result" | cut -d: -f1)
count_b=$(echo "$result" | cut -d: -f2)
done
echo "Counts: A=$count_a, B=$count_b"
echo "Using extended visitor:"
for el in "${elements[@]}"; do
extended_visitor "$el"
doneImplement iterator pattern for sequential access.
- Iterator:
create_iterator() { iterator_collection=("$@"); iterator_position=0; } - Has next:
has_next() { [ $iterator_position -lt ${#iterator_collection[@]} ]; } - Next:
next() { ... } - Reverse:
reverse_iterator() { for ((i=${#collection[@]}-1; i>=0; i--)); do ... }
#!/bin/bash
# Iterator pattern
declare -a iterator_collection
iterator_position=0
create_iterator() {
local collection=("$@")
iterator_collection=("${collection[@]}")
iterator_position=0
}
has_next() {
[ $iterator_position -lt ${#iterator_collection[@]} ]
}
next() {
if has_next; then
local item=${iterator_collection[$iterator_position]}
iterator_position=$((iterator_position + 1))
echo "$item"
fi
}
reverse_iterator() {
local collection=("$@")
for ((i=${#collection[@]}-1; i>=0; i--)); do
echo "${collection[$i]}"
done
}
filtered_iterator() {
local collection=("$@")
local predicate=$1
shift
for item in "$@"; do
if $predicate "$item"; then
echo "$item"
fi
done
}
skip_iterator() {
local n=$1
shift
local collection=("$@")
for ((i=n; i<${#collection[@]}; i++)); do
echo "${collection[$i]}"
done
}
# Usage
collection=("A" "B" "C" "D" "E")
create_iterator "${collection[@]}"
echo "Forward iteration:"
while has_next; do
echo "$(next)"
done
echo "Reverse iteration:"
reverse_iterator "${collection[@]}"
echo "Filtered iteration (length <= 1):"
filtered_iterator "test" "${collection[@]}"
echo "Skip 2:"
skip_iterator 2 "${collection[@]}"Implement template method with customizable steps.
- Template:
template_method() { step1; step2; step3; } - Default:
default_template() { step1() { echo "Step 1"; }; ... } - Logging:
logging_template() { step1() { echo "Logging: Step 1"; }; ... } - Data:
data_template() { ... }
#!/bin/bash
# Template Method pattern
template_method() {
step1
step2
step3
}
default_template() {
step1() { echo "Step 1"; }
step2() { echo "Step 2"; }
step3() { echo "Step 3"; }
template_method
}
logging_template() {
step1() { echo "Logging: Step 1"; }
step2() { echo "Logging: Step 2"; }
step3() { echo "Logging: Step 3"; }
template_method
}
data_template() {
local data=$1
step1() { echo "Processing data: $data - Step 1"; }
step2() { echo "Processing data: $data - Step 2"; }
step3() { echo "Processing data: $data - Step 3"; }
template_method
}
# Usage
echo "Using default template:"
default_template
echo "Using logging template:"
logging_template
echo "Using data processing template:"
data_template "example"Implement builder pattern for constructing complex objects.
- Product:
product_parts=() - Builder:
build_step_a() { product_parts+=("Part A"); } - Director:
build_minimal() { create_product; build_step_a; } - Custom:
build_custom() { for step in "$@"; do case $step in ... }
#!/bin/bash
# Builder pattern
declare -a product_parts
create_product() {
product_parts=()
}
add_part() {
product_parts+=("$1")
}
list_parts() {
echo "${product_parts[@]}"
}
builder() {
create_product
build_step_a() { add_part "Part A"; }
build_step_b() { add_part "Part B"; }
build_step_c() { add_part "Part C"; }
get_result() { echo "${product_parts[@]}"; }
}
director() {
build_minimal() {
create_product
build_step_a
}
build_full() {
create_product
build_step_a
build_step_b
build_step_c
}
build_custom() {
create_product
for step in "$@"; do
case $step in
A) build_step_a;;
B) build_step_b;;
C) build_step_c;;
esac
done
}
}
# Usage
echo "Minimal product:"
build_minimal
list_parts
echo "Full product:"
build_full
list_parts
echo "Custom product:"
create_product
build_step_c
build_step_a
list_parts
echo "Director custom:"
build_custom C A B
list_partsImplement prototype pattern for cloning objects.
- Prototype:
prototype() { echo "$1"; } - Clone:
clone_prototype() { echo "$1"; } - Deep clone:
deep_clone() { echo "$1"; } - Mutable:
set_mutable_data() { eval "$1='$2'"; }
#!/bin/bash
# Prototype pattern
prototype() {
local data=$1
echo "$data"
}
clone_prototype() {
local data=$1
echo "$data"
}
deep_clone() {
local data=$1
echo "$data"
}
mutable_prototype() {
local data=$1
echo "$data"
}
set_mutable_data() {
local name=$1
local data=$2
eval "$name='$data'"
}
get_mutable_data() {
local name=$1
eval "echo $$name"
}
# Usage
original="Original"
copy=$(clone_prototype "$original")
deep=$(deep_clone "$original")
echo "Original: $original"
echo "Copy: $copy"
echo "Deep copy: $deep"
mutable="Mutable"
set_mutable_data mutable_data "Original data"
echo "Original data: $(get_mutable_data mutable_data)"
set_mutable_data mutable_data "Modified data"
echo "Modified data: $(get_mutable_data mutable_data)"
cloned_mutable=$(clone_prototype "$mutable")
echo "Clone data: $cloned_mutable"
# Prototype with cache
declare -A prototype_cache
cached_prototype() {
local data=$1
if [ -z "${prototype_cache[$data]}" ]; then
prototype_cache[$data]="cached_$data"
fi
echo "${prototype_cache[$data]}"
}