Introduction to Bash Data Types
Bash is a weakly typed language, meaning variables don't have strict data types like in compiled languages (C, Java, Rust). However, Bash does support different data types through variable attributes and context-based interpretation. Understanding how Bash handles different types of data is crucial for writing robust scripts.
Key Concepts
- Weak Typing: Variables can hold any type of data
- Dynamic Typing: Type is determined by context and usage
- Variable Attributes: Declare can set specific behaviors
- Type Context: How data is interpreted depends on operation
- Implicit Conversion: Bash automatically converts between types as needed
1. Basic Data Types
Strings
#!/bin/bash # Strings are the most common data type in Bash name="John Doe" greeting='Hello World' empty_string="" # String with spaces multi_word="This is a string with spaces" # String with special characters special="Special chars: $ ` \" \\" # String concatenation first="Hello" second="World" combined="$first $second" echo "$combined" # Hello World # String length str="Hello" echo "Length: ${#str}" # 5 # Substring extraction str="Hello World" echo "${str:6}" # World (from position 6) echo "${str:0:5}" # Hello (5 chars from position 0) echo "${str: -5}" # World (last 5 chars) # String replacement str="Hello World" echo "${str/World/Universe}" # Hello Universe echo "${str//o/a}" # Hella Warld (replace all) # Case modification str="hello world" echo "${str^^}" # HELLO WORLD (uppercase) echo "${str^}" # Hello world (capitalize first) echo "${str,,}" # hello world (lowercase) Integers
#!/bin/bash # Integers are typically used in arithmetic contexts declare -i count=10 # Declare as integer count=5 # Still integer # count="hello" # Would set to 0 (invalid number) # Different integer bases declare -i dec=42 declare -i hex=0x2A declare -i oct=052 declare -i bin=2#101010 echo "Decimal: $dec" echo "Hexadecimal: $hex" echo "Octal: $oct" echo "Binary: $bin" # Integer operations a=10 b=3 # Arithmetic expansion sum=$((a + b)) diff=$((a - b)) prod=$((a * b)) quot=$((a / b)) rem=$((a % b)) pow=$((a ** b)) echo "a=$a, b=$b" echo "Sum: $sum" echo "Difference: $diff" echo "Product: $prod" echo "Quotient: $quot" echo "Remainder: $rem" echo "Power: $pow" # Increment/decrement x=5 ((x++)) # Post-increment echo "x: $x" # 6 ((++x)) # Pre-increment echo "x: $x" # 7 ((x+=5)) # Add 5 echo "x: $x" # 12 ((x-=3)) # Subtract 3 echo "x: $x" # 9 # Using let for arithmetic let y=10+5 echo "y: $y" let y+=3 echo "y: $y"
Floating Point Numbers
#!/bin/bash # Bash doesn't natively support floating point # Use bc (basic calculator) or awk # Using bc for floating point a=10.5 b=3.2 sum=$(echo "$a + $b" | bc) diff=$(echo "$a - $b" | bc) prod=$(echo "$a * $b" | bc) quot=$(echo "scale=2; $a / $b" | bc) echo "a=$a, b=$b" echo "Sum: $sum" echo "Difference: $diff" echo "Product: $prod" echo "Quotient: $quot" # More complex bc operations result=$(echo "scale=4; sqrt(25) + sin(0.5)" | bc -l) echo "Complex result: $result" # Using awk for floating point result=$(awk "BEGIN {printf \"%.2f\", $a * $b}") echo "Awk result: $result" # Comparison with floating point if (( $(echo "$a > $b" | bc -l) )); then echo "$a is greater than $b" fi # Scientific notation large=1.5e3 small=2.5e-2 echo "Large: $large" echo "Small: $small" Booleans
#!/bin/bash # Bash doesn't have a boolean type # Convention: use 0 for true, 1 for false (opposite of many languages) # Test commands return 0 for success, non-zero for failure # Boolean variables (using strings) success="true" failure="false" if [ "$success" = "true" ]; then echo "Operation succeeded" fi # Using exit codes as booleans function check_file() { if [ -f "$1" ]; then return 0 # True (success) else return 1 # False (failure) fi } if check_file "/etc/passwd"; then echo "File exists" fi # Boolean expressions # && for AND, || for OR if [ -f "/etc/passwd" ] && [ -r "/etc/passwd" ]; then echo "File exists and is readable" fi if [ -f "/etc/passwd" ] || [ -f "/etc/shadow" ]; then echo "At least one file exists" fi # Using arithmetic for boolean logic true_val=1 false_val=0 if (( true_val )); then echo "This executes (1 is true)" fi if (( ! false_val )); then echo "This executes (not false is true)" fi 2. Variable Attributes with declare
declare Options
#!/bin/bash # declare -i: Integer attribute declare -i number=42 number="25" # Valid number="abc" # Becomes 0 (invalid number) echo "Number: $number" # declare -r: Read-only (constant) declare -r PI=3.14159 # PI=3.14 # Error: readonly variable # declare -l: Convert to lowercase declare -l lowercase="HELLO WORLD" echo "$lowercase" # hello world # declare -u: Convert to uppercase declare -u uppercase="hello world" echo "$uppercase" # HELLO WORLD # declare -a: Array declare -a fruits=("apple" "banana" "cherry") # declare -A: Associative array declare -A user=( ["name"]="John" ["age"]="30" ["city"]="New York" ) # declare -x: Export variable declare -x DATABASE_URL="postgres://localhost:5432/db" # Now available to child processes # declare -p: Print attributes declare -p PI declare -p lowercase # Multiple attributes declare -r -i MAX_RETRIES=5 # or declare -ri MAX_CONNECTIONS=100 # Show all declared variables declare -p Type Declaration Examples
#!/bin/bash # Integer with default value declare -i count=0 for i in {1..5}; do ((count++)) done echo "Count: $count" # Read-only configuration declare -r CONFIG_FILE="/etc/myapp/config.conf" declare -r LOG_DIR="/var/log/myapp" declare -r MAX_LOG_SIZE=10485760 # Case-insensitive string declare -l input="HELLO" if [ "$input" = "hello" ]; then echo "Matched case-insensitively" fi # Export for child processes declare -x PATH="/custom/bin:$PATH" declare -x MYAPP_ENV="production" # Combining attributes declare -ri HTTP_PORT=8080 # HTTP_PORT=9090 # Error: read-only # Array with declare declare -a colors=("red" "green" "blue") declare -p colors 3. Arrays
Indexed Arrays
#!/bin/bash # Creating indexed arrays fruits=("apple" "banana" "cherry") numbers=([0]=10 [1]=20 [2]=30) mixed=("hello" 42 3.14 "world") # Adding elements fruits[3]="date" fruits+=("elderberry" "fig") # Append multiple # Accessing elements echo "First fruit: ${fruits[0]}" echo "Last fruit: ${fruits[-1]}" echo "All fruits: ${fruits[@]}" echo "All fruits (as string): ${fruits[*]}" # Array length echo "Number of fruits: ${#fruits[@]}" # Array indices echo "Indices: ${!fruits[@]}" # Looping through array for fruit in "${fruits[@]}"; do echo "Fruit: $fruit" done # Looping with indices for i in "${!fruits[@]}"; do echo "Index $i: ${fruits[$i]}" done # Slicing arrays subset=("${fruits[@]:1:3}") # Elements 1-3 echo "Subset: ${subset[@]}" # Removing elements unset "fruits[1]" # Remove element at index 1 echo "After removal: ${fruits[@]}" # Replace element fruits[0]="apricot" echo "After replace: ${fruits[@]}" # Check if element exists if [ -n "${fruits[2]+x}" ]; then echo "Element at index 2 exists" fi # Copy array new_array=("${fruits[@]}") # Join array elements joined=$(IFS=, ; echo "${fruits[*]}") echo "Joined: $joined" Associative Arrays (Hash Maps)
#!/bin/bash # Declare associative array declare -A user declare -A config=( ["host"]="localhost" ["port"]="8080" ["user"]="admin" ) # Adding key-value pairs user["name"]="John Doe" user["email"]="[email protected]" user["age"]=30 user["active"]=true # Accessing values echo "Name: ${user[name]}" # Quotes optional for simple keys echo "Email: ${user[email]}" echo "All values: ${user[@]}" echo "All keys: ${!user[@]}" # Number of entries echo "User has ${#user[@]} fields" # Looping through associative array for key in "${!user[@]}"; do echo "Key: $key, Value: ${user[$key]}" done # Check if key exists if [[ -v user["email"] ]]; then echo "Email key exists" fi # Alternative check if [ -n "${user["phone"]+x}" ]; then echo "Phone exists" else echo "Phone does not exist" fi # Remove key-value pair unset user["age"] # Nested structures (simulated) declare -A users users["john,name"]="John Doe" users["john,email"]="[email protected]" users["jane,name"]="Jane Smith" users["jane,email"]="[email protected]" # Access nested-like data echo "John's email: ${users["john,email"]}" # Merge associative arrays declare -A defaults=( ["host"]="localhost" ["port"]="8080" ["debug"]="false" ) declare -A overrides=( ["port"]="9090" ["user"]="custom" ) # Merge (override takes precedence) declare -A merged for key in "${!defaults[@]}"; do merged[$key]="${defaults[$key]}" done for key in "${!overrides[@]}"; do merged[$key]="${overrides[$key]}" done
4. Advanced String Operations
String Manipulation
#!/bin/bash # Pattern matching str="hello_world_example.txt" # Remove shortest prefix pattern echo "${str#*_}" # world_example.txt echo "${str#*_*_}" # example.txt # Remove longest prefix pattern echo "${str##*_}" # txt # Remove shortest suffix pattern echo "${str%_*}" # hello_world_example echo "${str%_*_*}" # hello_world # Remove longest suffix pattern echo "${str%%.*}" # hello_world_example # Search and replace str="apple apple apple" echo "${str/apple/orange}" # orange apple apple echo "${str//apple/orange}" # orange orange orange echo "${str/#apple/orange}" # orange apple apple (start) echo "${str/%apple/orange}" # apple apple orange (end) # Case conversion str="Hello World" echo "${str,,}" # hello world echo "${str,}" # hello World (first only) echo "${str^^}" # HELLO WORLD echo "${str^}" # Hello World (first only) # Default values unset var echo "${var:-default}" # default (if unset) echo "${var:=default}" # default (assigns if unset) echo "${var:+exists}" # (empty) - uses if set echo "${var:?error}" # error (if unset) # String length str="Hello" echo "${#str}" # 5 # Substring with pattern str="abc123def456" echo "${str//[0-9]/}" # abcdef (remove digits) echo "${str//[a-z]/}" # 123456 (remove letters) Regular Expressions
#!/bin/bash # Using =~ for regex matching email="[email protected]" if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo "Valid email format" fi # Capturing groups phone="123-456-7890" if [[ "$phone" =~ ^([0-9]{3})-([0-9]{3})-([0-9]{4})$ ]]; then echo "Area code: ${BASH_REMATCH[1]}" echo "Prefix: ${BASH_REMATCH[2]}" echo "Line number: ${BASH_REMATCH[3]}" fi # Multiple matches text="The numbers are 123, 456, and 789" pattern="[0-9]+" while [[ "$text" =~ $pattern ]]; do echo "Found: ${BASH_REMATCH[0]}" text="${text#*"${BASH_REMATCH[0]}"}" done # Validating input validate_input() { local input="$1" local type="$2" case "$type" in email) [[ "$input" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]] ;; phone) [[ "$input" =~ ^[0-9]{3}-[0-9]{3}-[0-9]{4}$ ]] ;; ip) [[ "$input" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] ;; date) [[ "$input" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] ;; esac } if validate_input "[email protected]" "email"; then echo "Valid email" fi
5. Numbers and Arithmetic
Integer Operations
#!/bin/bash # Basic arithmetic a=10 b=3 # Different arithmetic syntaxes echo $((a + b)) echo $[a + b] # Older syntax ((result = a + b)) echo "$result" # Increment/decrement ((a++)) ((b--)) ((a += 5)) ((b *= 2)) # Bitwise operations x=12 # 1100 in binary y=10 # 1010 in binary echo "x & y: $((x & y))" # 8 (1000) echo "x | y: $((x | y))" # 14 (1110) echo "x ^ y: $((x ^ y))" # 6 (0110) echo "~x: $((~x))" # -13 (two's complement) echo "x << 1: $((x << 1))" # 24 (11000) echo "x >> 1: $((x >> 1))" # 6 (0110) # Logical operations x=5 y=0 echo "x && y: $((x && y))" # 0 (false) echo "x || y: $((x || y))" # 1 (true) echo "!x: $((!x))" # 0 (false) echo "!y: $((!y))" # 1 (true) # Ternary-like operation result=$((a > b ? a : b)) echo "Max: $result" # Assignment in arithmetic ((count=0)) ((count++)) ((count+=5)) ((count=count*2)) echo "Count: $count"
Advanced Math with bc
#!/bin/bash # Basic bc operations echo "scale=2; 10 / 3" | bc # Variables in bc x=5.5 y=2.3 result=$(bc << EOF scale=4 x = $x y = $y x * y + sqrt(x) EOF ) echo "Result: $result" # Math functions cat << EOF | bc -l scale=4 s = s(0.5) # sine c = c(0.5) # cosine l = l(2.71828) # natural log e = e(1) # exponential a = a(1) # arctangent print "sin(0.5) = ", s, "\n" print "cos(0.5) = ", c, "\n" print "ln(e) = ", l, "\n" print "e^1 = ", e, "\n" print "atan(1) = ", a, "\n" EOF # Conditional in bc check_range() { local value="$1" local min="$2" local max="$3" bc << EOF if ($value >= $min && $value <= $max) 1 else 0 EOF } if [ "$(check_range 5.5 0 10)" -eq 1 ]; then echo "Value in range" fi # Loop in bc bc << EOF for (i=1; i<=5; i++) { print "Square of ", i, " = ", i^2, "\n" } EOF # Custom math functions bc << EOF define f(x) { return (x * x + 2 * x + 1) } for (i=0; i<=5; i++) { print "f(", i, ") = ", f(i), "\n" } EOF 6. Type Conversion
Implicit vs Explicit Conversion
#!/bin/bash # Implicit conversion in arithmetic str="42" num=$((str + 10)) # String to integer echo "$num" # 52 # Implicit in comparisons if [[ "10" -eq 10 ]]; then # String to integer echo "Equal" fi # Explicit conversion # String to integer int_value=$((42)) int_value=$(echo "10" | bc) # Integer to string str_value="$((42))" str_value=$(printf "%d" 42) # Float to string float_str=$(printf "%.2f" 3.14159) # Base conversion # Decimal to hex dec=255 hex=$(printf "%x" "$dec") echo "Hex: $hex" # Hex to decimal hex="FF" dec=$((16#$hex)) echo "Decimal: $dec" # Binary to decimal binary=1010 dec=$((2#$binary)) echo "Decimal: $dec" # ASCII to character ascii=65 char=$(printf "\\$(printf "%03o" "$ascii")") echo "Char: $char" # Character to ASCII char='A' ascii=$(printf "%d" "'$char") echo "ASCII: $ascii"
printf for Type Formatting
#!/bin/bash # printf for formatted output printf "Integer: %d\n" 42 printf "Float: %.2f\n" 3.14159 printf "String: %s\n" "hello" printf "Hex: %x\n" 255 printf "Octal: %o\n" 255 printf "Character: %c\n" "A" # Width and alignment printf "|%10s|\n" "right" # Right aligned printf "|%-10s|\n" "left" # Left aligned printf "|%10.2f|\n" 3.14159 # Width and precision # Multiple arguments printf "Name: %s, Age: %d\n" "John" 30 # Zero padding printf "ID: %05d\n" 42 # 00042 # Sign printf "%+d\n" 42 # +42 printf "%+d\n" -42 # -42 # Space padding printf "% d\n" 42 # " 42" printf "% d\n" -42 # "-42" # Printf into variable name=$(printf "User_%03d" 5) echo "$name" # User_005 # Complex formatting printf "|%-10s|%8s|%8s|\n" "Name" "Age" "Score" printf "|%-10s|%8d|%8.1f|\n" "John" 30 95.5 printf "|%-10s|%8d|%8.1f|\n" "Jane" 28 97.8
7. Type Checking and Testing
Testing Variable Types
#!/bin/bash # Check if variable is set if [ -v myvar ]; then echo "myvar is set" fi # Check if variable is empty if [ -z "$myvar" ]; then echo "myvar is empty or unset" fi # Check if variable is non-empty if [ -n "$myvar" ]; then echo "myvar has content" fi # Check if value is integer is_integer() { [[ "$1" =~ ^-?[0-9]+$ ]] } # Check if value is float is_float() { [[ "$1" =~ ^-?[0-9]*\.?[0-9]+$ ]] } # Check if value is alphabetic is_alpha() { [[ "$1" =~ ^[a-zA-Z]+$ ]] } # Check if value is alphanumeric is_alnum() { [[ "$1" =~ ^[a-zA-Z0-9]+$ ]] } # Test functions test_values() { local values=("42" "-10" "3.14" "-2.5" "abc" "123abc" "" " " "$@") for val in "${values[@]}"; do echo "Testing '$val':" echo " Integer? $(is_integer "$val" && echo yes || echo no)" echo " Float? $(is_float "$val" && echo yes || echo no)" echo " Alpha? $(is_alpha "$val" && echo yes || echo no)" echo " Alnum? $(is_alnum "$val" && echo yes || echo no)" echo done } # Run tests test_values Type Attributes Introspection
#!/bin/bash # Check if variable has integer attribute declare -i int_var=42 str_var="hello" if [[ "$(declare -p int_var 2>/dev/null)" == *"declare -i"* ]]; then echo "int_var is integer" fi # Check if variable is array declare -a array_var=(1 2 3) if [[ "$(declare -p array_var 2>/dev/null)" == *"declare -a"* ]]; then echo "array_var is indexed array" fi # Check if variable is associative array declare -A assoc_var=([key]=value) if [[ "$(declare -p assoc_var 2>/dev/null)" == *"declare -A"* ]]; then echo "assoc_var is associative array" fi # Check if variable is readonly declare -r readonly_var="cannot change" if [[ "$(declare -p readonly_var 2>/dev/null)" == *"declare -r"* ]]; then echo "readonly_var is readonly" fi # Function to get variable type get_type() { local var_name="$1" local decl=$(declare -p "$var_name" 2>/dev/null) if [[ -z "$decl" ]]; then echo "unset" elif [[ "$decl" == *"declare -A"* ]]; then echo "associative array" elif [[ "$decl" == *"declare -a"* ]]; then echo "indexed array" elif [[ "$decl" == *"declare -i"* ]]; then echo "integer" elif [[ "$decl" == *"declare -r"* ]]; then echo "readonly" elif [[ "$decl" == *"declare -x"* ]]; then echo "exported" else echo "string" fi } # Test type detection declare -i num=42 declare -a arr=(1 2 3) declare -A map=([k]=v) declare -r ro="constant" declare -x exp="exported" normal="value" for var in num arr map ro exp normal; do echo "$var: $(get_type "$var")" done 8. Special Types and Structures
Here Documents
#!/bin/bash # Basic here document cat << EOF This is a here document It can contain multiple lines Variables like $HOME are expanded EOF # Here document with no variable expansion cat << 'EOF' Variables like $HOME are NOT expanded Commands like `date` are NOT executed EOF # Here document with tabs ignored cat <<- EOF This line starts with tabs They will be removed Useful for indented code EOF # Here string cat <<< "This is a here string" # Assign here document to variable sql=$(cat << EOF SELECT * FROM users WHERE active = true ORDER BY name; EOF ) echo "$sql" # Here document with command bc << EOF scale=2 a = 5.5 b = 3.2 a * b EOF # Multi-line string with here doc message=$(cat << 'EOF' This is a multi-line message EOF ) echo "$message"
Process Substitution
#!/bin/bash # Process substitution for comparing outputs diff <(ls /tmp) <(ls /var/tmp) # Using process substitution with while while read line; do echo "Processed: $line" done < <(find . -name "*.txt" -type f) # Multiple process substitutions paste <(ls -1) <(ls -1 | wc -l) # Redirect both stdout and stderr command < <(echo input) 2> >(tee error.log >&2) # Process substitution with arrays mapfile -t files < <(find . -type f -name "*.sh") echo "Found ${#files[@]} shell scripts" # Complex example while IFS= read -r user; do while IFS= read -r process; do echo "$user: $process" done < <(ps -u "$user" -o comm=) done < <(cut -d: -f1 /etc/passwd | head -5) 9. Type Declaration and Best Practices
Declaring Variables with Types
#!/bin/bash # Best practices for type declaration # Constants readonly MAX_RETRIES=3 readonly CONFIG_FILE="/etc/myapp.conf" readonly -a VALID_OPTIONS=("start" "stop" "restart") # Configuration with defaults declare -A CONFIG=( ["host"]="localhost" ["port"]="8080" ["debug"]="false" ) # Export environment variables export DATABASE_URL="postgresql://localhost/mydb" export PATH="/custom/bin:$PATH" # Integer counters declare -i error_count=0 declare -i total_processed=0 # Arrays for collections declare -a users=() declare -a processes=() # Read-only arrays readonly -a DAYS=("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun") # Typed functions function get_integer() { local -i result result=$(( $1 * 2 )) echo "$result" } function get_string() { local result result="Processed: $1" echo "$result" } # Usage value=$(get_integer 5) message=$(get_string "test") echo "$value, $message" Type Safety Patterns
#!/bin/bash # Type checking functions assert_integer() { if ! [[ "$1" =~ ^-?[0-9]+$ ]]; then echo "Error: Expected integer, got: $1" >&2 return 1 fi return 0 } assert_positive() { if ! assert_integer "$1" || [ "$1" -le 0 ]; then echo "Error: Expected positive integer" >&2 return 1 fi return 0 } assert_string() { if [ -z "$1" ]; then echo "Error: Expected non-empty string" >&2 return 1 fi return 0 } assert_array() { local -n arr=$1 if [[ ! "$(declare -p $1 2>/dev/null)" == *"declare -a"* ]]; then echo "Error: Expected array" >&2 return 1 fi return 0 } # Safe arithmetic with validation safe_add() { assert_integer "$1" || return 1 assert_integer "$2" || return 1 echo $(( $1 + $2 )) } # Safe string operations safe_upper() { assert_string "$1" || return 1 echo "${1^^}" } # Usage with error handling result=$(safe_add 5 10) || exit 1 echo "Sum: $result" upper=$(safe_upper "hello") || exit 1 echo "Upper: $upper" 10. Practical Examples
Data Validation Functions
#!/bin/bash # Comprehensive validation library # Type validators validate_email() { local email="$1" [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]] } validate_phone() { local phone="$1" # Strip common formatting phone="${phone//[^0-9]/}" [[ ${#phone} -eq 10 ]] || [[ ${#phone} -eq 11 && "${phone:0:1}" == "1" ]] } validate_ip() { local ip="$1" local IFS='.' read -ra octets <<< "$ip" if [ ${#octets[@]} -ne 4 ]; then return 1 fi for octet in "${octets[@]}"; do if ! [[ "$octet" =~ ^[0-9]+$ ]] || [ "$octet" -lt 0 ] || [ "$octet" -gt 255 ]; then return 1 fi if [ "${#octet}" -gt 1 ] && [ "${octet:0:1}" = "0" ]; then return 1 # No leading zeros fi done return 0 } validate_date() { local date="$1" local format="${2:-%Y-%m-%d}" if command -v date >/dev/null 2>&1; then if date -d "$date" "+$format" >/dev/null 2>&1; then return 0 fi fi return 1 } validate_url() { local url="$1" [[ "$url" =~ ^https?://[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(/.*)?$ ]] } validate_credit_card() { local cc="${1//[^0-9]/}" # Luhn algorithm local sum=0 local len=${#cc} local parity=$((len % 2)) for ((i=0; i<len; i++)); do local digit=${cc:$i:1} if [ $((i % 2)) -eq $parity ]; then digit=$((digit * 2)) [ $digit -gt 9 ] && digit=$((digit - 9)) fi sum=$((sum + digit)) done [ $((sum % 10)) -eq 0 ] } # Test validators test_validator() { local validator="$1" shift echo "Testing $validator:" for value in "$@"; do if "$validator" "$value"; then echo " ✓ $value" else echo " ✗ $value" fi done echo } # Run tests test_validator validate_email \ "[email protected]" \ "invalid.email" \ "[email protected]" \ "[email protected]" test_validator validate_phone \ "555-123-4567" \ "5551234567" \ "1-555-123-4567" \ "123" \ "555-123-45678" test_validator validate_ip \ "192.168.1.1" \ "256.0.0.0" \ "1.2.3.4" \ "01.02.03.04" \ "999.999.999.999" Type Conversion Library
#!/bin/bash # Comprehensive type conversion library # String to number conversions str_to_int() { local str="$1" local default="${2:-0}" # Remove non-numeric characters except leading minus str="${str//[^0-9-]/}" if [[ "$str" =~ ^-?[0-9]+$ ]]; then echo "$str" else echo "$default" fi } str_to_float() { local str="$1" local default="${2:-0}" # Keep digits, decimal point, and leading minus str=$(echo "$str" | sed 's/[^0-9.-]//g') if [[ "$str" =~ ^-?[0-9]*\.?[0-9]+$ ]]; then echo "$str" else echo "$default" fi } # Number to string conversions int_to_hex() { printf "%x" "$1" } int_to_oct() { printf "%o" "$1" } int_to_bin() { local num="$1" local bin="" while [ "$num" -gt 0 ]; do bin="$((num & 1))$bin" num="$((num >> 1))" done echo "${bin:-0}" } float_to_str() { local float="$1" local precision="${2:-2}" printf "%.${precision}f" "$float" 2>/dev/null || echo "0" } # Unit conversions bytes_to_human() { local bytes="$1" local units=("B" "KB" "MB" "GB" "TB" "PB") local unit=0 while [ "$bytes" -ge 1024 ] && [ $unit -lt 5 ]; do bytes=$((bytes / 1024)) unit=$((unit + 1)) done echo "$bytes ${units[$unit]}" } human_to_bytes() { local human="$1" local number=$(echo "$human" | grep -o '^[0-9.]*') local unit=$(echo "$human" | grep -o '[KMGTP]B\?$') case "$unit" in B|b) factor=1 ;; KB|Kb) factor=1024 ;; MB|Mb) factor=$((1024 * 1024)) ;; GB|Gb) factor=$((1024 * 1024 * 1024)) ;; TB|Tb) factor=$((1024 * 1024 * 1024 * 1024)) ;; *) factor=1 ;; esac echo "$(echo "$number * $factor" | bc 2>/dev/null || echo 0)" } # Temperature conversions celsius_to_fahrenheit() { echo "scale=2; $1 * 9/5 + 32" | bc } fahrenheit_to_celsius() { echo "scale=2; ($1 - 32) * 5/9" | bc } celsius_to_kelvin() { echo "scale=2; $1 + 273.15" | bc } # Test conversions echo "str_to_int '42abc': $(str_to_int '42abc')" echo "str_to_float '3.14xyz': $(str_to_float '3.14xyz')" echo "int_to_hex 255: $(int_to_hex 255)" echo "int_to_bin 42: $(int_to_bin 42)" echo "bytes_to_human 1048576: $(bytes_to_human 1048576)" echo "human_to_bytes '1.5 GB': $(human_to_bytes '1.5 GB')" echo "25°C to °F: $(celsius_to_fahrenheit 25)°F" 11. Performance Considerations
Type Impact on Performance
#!/bin/bash # Benchmark different operations benchmark() { local iterations="${1:-10000}" echo "Benchmark with $iterations iterations" echo "===================================" # String concatenation start=$SECONDS str="" for ((i=0; i<iterations; i++)); do str+="a" done echo "String concatenation: $((SECONDS - start))s" # Integer arithmetic start=$SECONDS sum=0 for ((i=0; i<iterations; i++)); do ((sum += i)) done echo "Integer arithmetic: $((SECONDS - start))s" # Array operations start=$SECONDS arr=() for ((i=0; i<iterations; i++)); do arr+=($i) done echo "Array append: $((SECONDS - start))s" # String operations start=$SECONDS text="Hello World" for ((i=0; i<iterations; i++)); do upper="${text^^}" lower="${text,,}" done echo "String operations: $((SECONDS - start))s" } # Memory usage check_memory() { local var_name="$1" local var_value="$2" # This is approximate eval "$var_name=\"$var_value\"" # Get process memory before mem_before=$(ps -o rss= -p $$) # Force variable into memory eval "length=\${#$var_name}" # Get process memory after mem_after=$(ps -o rss= -p $$) echo "Memory used by $var_name: $((mem_after - mem_before)) KB" } # Test memory usage check_memory "small" "Hello" check_memory "medium" "$(printf 'x%.0s' {1..1000})" check_memory "large" "$(printf 'x%.0s' {1..100000})" Optimization Tips
#!/bin/bash # Use integer types for counters # Slow count=0 for i in {1..1000}; do count=$((count + 1)) done # Fast declare -i count=0 for i in {1..1000}; do ((count++)) done # Avoid unnecessary forks # Slow - forks subshell length=$(echo -n "$str" | wc -c) # Fast - uses parameter expansion length=${#str} # Use arrays for collections # Slow - using strings items="a b c d e" for item in $items; do process "$item" done # Fast - using arrays items=("a" "b" "c" "d" "e") for item in "${items[@]}"; do process "$item" done # Avoid unnecessary external commands # Slow upper=$(echo "$str" | tr '[:lower:]' '[:upper:]') # Fast (bash 4+) upper="${str^^}" # Use built-in pattern matching # Slow if echo "$str" | grep -q "^[0-9]\+$"; then echo "Is number" fi # Fast if [[ "$str" =~ ^[0-9]+$ ]]; then echo "Is number" fi # Batch operations # Slow - one fork per file for file in *.txt; do wc -l "$file" done # Fast - one fork for all files wc -l *.txt 12. Common Patterns and Best Practices
Type Declaration Patterns
#!/bin/bash # Configuration with defaults : "${APP_NAME:=MyApp}" : "${APP_PORT:=8080}" : "${APP_DEBUG:=false}" # Type-safe configuration declare -A CONFIG=( [name]="${APP_NAME}" [port]="${APP_PORT}" [debug]="${APP_DEBUG}" ) # Validation function validate_config() { local errors=() # Validate port is integer if ! [[ "${CONFIG[port]}" =~ ^[0-9]+$ ]]; then errors+=("Port must be integer") fi # Validate debug is boolean if [[ ! "${CONFIG[debug]}" =~ ^(true|false)$ ]]; then errors+=("Debug must be true/false") fi # Report errors if [ ${#errors[@]} -gt 0 ]; then printf '%s\n' "${errors[@]}" return 1 fi return 0 } # Data structures declare -a queue=() # FIFO queue declare -a stack=() # LIFO stack # Queue operations queue_push() { queue+=("$1") } queue_pop() { if [ ${#queue[@]} -gt 0 ]; then local first="${queue[0]}" queue=("${queue[@]:1}") echo "$first" fi } # Stack operations stack_push() { stack+=("$1") } stack_pop() { if [ ${#stack[@]} -gt 0 ]; then local last_idx=$(( ${#stack[@]} - 1 )) local last="${stack[$last_idx]}" unset "stack[$last_idx]" echo "$last" fi } Error Handling with Types
#!/bin/bash # Error types declare -A ERROR_CODES=( [SUCCESS]=0 [INVALID_INPUT]=1 [FILE_NOT_FOUND]=2 [PERMISSION_DENIED]=3 [CONFIG_ERROR]=4 ) # Error messages declare -A ERROR_MESSAGES=( [${ERROR_CODES[SUCCESS]}]="Success" [${ERROR_CODES[INVALID_INPUT]}]="Invalid input" [${ERROR_CODES[FILE_NOT_FOUND]}]="File not found" [${ERROR_CODES[PERMISSION_DENIED]}]="Permission denied" [${ERROR_CODES[CONFIG_ERROR]}]="Configuration error" ) # Function returning typed error read_config() { local config_file="$1" if [ ! -f "$config_file" ]; then return ${ERROR_CODES[FILE_NOT_FOUND]} fi if [ ! -r "$config_file" ]; then return ${ERROR_CODES[PERMISSION_DENIED]} fi # Process config... return ${ERROR_CODES[SUCCESS]} } # Handle error with type read_config "/etc/myapp.conf" result=$? if [ $result -ne 0 ]; then echo "Error: ${ERROR_MESSAGES[$result]}" exit $result fi Conclusion
Bash's approach to data types is flexible but requires understanding:
Key Takeaways
- Weak Typing: Variables can hold any type, context determines interpretation
- String Focus: Most data in Bash is handled as strings
- Integer Support: Arithmetic operations work on integer types
- Arrays: Both indexed and associative arrays available
- Type Attributes:
declarecan enforce certain behaviors - Context Matters: How data is used determines its effective type
Type Summary
| Type | Declaration | Usage | Features |
|---|---|---|---|
| String | var="value" | ${var} | Pattern matching, substitution |
| Integer | declare -i | $((var)) | Arithmetic operations |
| Float | N/A | Use bc | External tool required |
| Array | declare -a | ${arr[@]} | Indexed by number |
| Associative | declare -A | ${map[key]} | Key-value pairs |
| Readonly | declare -r | $var | Constant values |
| Exported | declare -x | $var | Environment variables |
Best Practices
- Use
declarefor type enforcement when appropriate - Validate input types before processing
- Use arrays for collections of items
- Use associative arrays for key-value data
- Handle type conversions explicitly
- Consider performance of type operations
- Use parameter expansion instead of external commands
- Document expected types in functions
Understanding Bash's data type system, even though it's weakly typed, helps write more robust and predictable scripts. While you don't need to declare types most of the time, knowing how to use type attributes and when to validate types can prevent many common scripting errors.
Complete Bash Exercises with Solutions
Practice Bash scripting with solved exercises to improve your command-line and scripting skills.
Link: https://macronepal.com/complete-bash-exercises-with-solutions/
Complete Guide to Bash Crontab Command
Learn how to schedule and automate tasks efficiently using the Bash crontab command.
Link: https://macronepal.com/complete-guide-to-bash-crontab-command-schedule-tasks/
Complete Guide to Bash Arrays
Understand how to create, manage, and use arrays in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-arrays/
Bash Quiz: Test Your Knowledge
Test your Bash scripting knowledge with quizzes and practical questions.
Link: https://macronepal.com/bash-quiz-test-your-knowledge/
Complete Guide to Bash Functions
Learn how to write reusable Bash functions for cleaner and efficient scripts.
Link: https://macronepal.com/complete-guide-to-bash-functions/
Complete Guide to Bash Loops
Master loops in Bash for repetitive tasks and automation.
Link: https://macronepal.com/complete-guide-to-bash-loops/
Complete Guide to Bash If-Else Statements
Learn decision-making in Bash using conditional statements.
Link: https://macronepal.com/complete-guide-to-bash-ifelse-statements/
Complete Guide to Bash Operators
Explore arithmetic, logical, and comparison operators in Bash.
Link: https://macronepal.com/complete-guide-to-bash-operators/
Complete Guide to Bash Data Types
Understand variables and different data types used in Bash scripting.
Link: https://macronepal.com/complete-guide-to-bash-data-types/
Complete Guide to Bash Variables
Learn how to declare, assign, and use variables in Bash scripts.
Link: https://macronepal.com/complete-guide-to-bash-variables/
Complete Guide to Bash Scripting
A complete introduction to writing Bash scripts for automation.
Link: https://macronepal.com/complete-guide-to-bash-scripting/
Complete Guide to Basic Bash Syntax
Learn the fundamental syntax needed to write Bash commands and scripts.
Link: https://macronepal.com/complete-guide-to-basic-bash-syntax/
Complete Guide to Bash Chgrp Command
Learn how to change group ownership of files and directories.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership-2/
Complete Guide to Bash Chgrp Command (Alternate)
Another detailed explanation of Bash group ownership management.
Link: https://macronepal.com/complete-guide-to-bash-chgrp-command-change-group-ownership/
Complete Guide to Bash File Permissions and Ownership
Understand Linux file permissions and ownership management.
Link: https://macronepal.com/complete-guide-to-bash-file-permissions-and-ownership/
Complete Guide to Bash Chmod Command
Learn how to modify file permissions using chmod.
Link: https://macronepal.com/complete-guide-to-bash-chmod-command-change-file-permissions/
Complete Guide to Bash Chown Command
Change file ownership using the Bash chown command.
Link: https://macronepal.com/complete-guide-to-bash-chown-command-change-file-ownership/
Complete Guide to Bash SCP Command
Securely transfer files between systems using SCP.
Link: https://macronepal.com/complete-guide-to-bash-scp-command-secure-copy/
Complete Guide to Bash SSH Command
Learn secure remote access and server management using SSH.
Link: https://macronepal.com/complete-guide-to-bash-ssh-command/
Bash Wget Command Complete Guide
Download files from the internet using the wget command.
Link: https://macronepal.com/bash-wget-command-complete-guide-to-non-interactive-network-downloader/
