🧙 Shell Tips for Linux Wizards

linux, shell, script, bash

saving your time: some tips for linux shell

Index #

LOOPING: command x script #

command #

for i in {1..5}; do echo $i; done

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

script #

#!/bin/bash

echo "Counting to 5 with a for loop:"
for i in {1..5}; do
    echo $i
done

echo "Counting to 5 with a while loop:"
count=1
while [ $count -le 5 ]; do
    echo $count
    ((count++))
done

Signals #

Some scenarios that make sense to deal with signals..

SIGINT #

This script creates a temporary file and displays a goodbye message when it receives a SIGINT signal (e.g., when the user presses Ctrl+C).

#!/bin/bash

# Function that will be called when the script receives the SIGINT signal
function exitMsg {
    echo "you sent a signal to end, byby !!"
    # command here
    exit
}

# Sets up the trap to call the cleanup function when the script receives SIGINT
trap exitMsg SIGINT

sleep 5

echo "Creating a temporary file..."
touch /tmp/apolzek || exit 1  # Creates a temporary file or exits with an error

SIGTERM #

This script starts a web server that runs indefinitely and shuts down gracefully when it receives a SIGTERM signal. kill <PID>

#!/bin/bash

# Function called upon receiving SIGTERM
function terminateMsg {
    echo "Received SIGTERM, shutting down the server gracefully..."
    # Here you can add commands to close connections or save the state
    exit
}

# Sets up the trap for SIGTERM
trap terminateMsg SIGTERM

echo "Starting web server..."
while true; do
    echo "Server is running... (PID: $$)"
    sleep 2  # Simulates the server's running time
done

SIGHUB #

This script starts a daemon that runs indefinitely and reloads its configuration when it receives a SIGHUP signal. kill -HUP <PID>

#!/bin/bash

# Function called upon receiving SIGHUP
function reloadMsg {
    echo "Received SIGHUP, reloading configuration..."
    # Here you can add commands to reload the configurations
    # Example: source /etc/mydaemon/config.conf
}

# Sets up the trap for SIGHUP
trap reloadMsg SIGHUP

echo "Starting my daemon..."
while true; do
    echo "Daemon is running... (PID: $$)"
    sleep 5  # Simulates the daemon's running time
done

Background processes #

#!/bin/bash

echo "Starting background processes..."

# Process 1
sleep 3 &  # This simulates a long-running task
pid1=$!  # Get the process ID of process 1

# Process 2
sleep 9 &  # This simulates a shorter task
pid2=$!  # Get the process ID of process 2

# Wait for process 1 to finish and notify
wait $pid1
echo "Process 1 has completed."

# Wait for process 2 to finish and notify
wait $pid2
echo "Process 2 has completed."

echo "All processes have finished."

Debugging #

#!/bin/bash

set -x  # Enable debugging mode

echo "Starting the script..."
echo "Doing something..."
sleep 1
echo "Ending the script."

set +x  # Disable debugging mode

echo "now debugging mode is disable"
echo "did you understand ?"

using pipefail..

#!/bin/bash
set -xeuo pipefail

# Uninitialized variable (throws an error with `set -u`)
echo "Attempting to access an uninitialized variable..."
echo "Variable value: $UNINITIALIZED_VAR"

# This command will never be executed due to the previous error
echo "End of script."

String Manipulation and Substitution #

#!/bin/bash

# Defining an original string
original="Linux is amazing!"

# Converting to uppercase
uppercase=${original^^}
echo "Uppercase: $uppercase"
# Output: Uppercase: LINUX IS AMAZING!

## another way
echo "Linux is amazing!" | tr '[:lower:]' '[:upper:]'
echo "Linux is amazing!" | awk '{ print toupper($0) }'

# Converting to lowercase
lowercase=${original,,}
echo "Lowercase: $lowercase"
# Output: Lowercase: linux is amazing!

# Replacing part of the string
modified=${original//amazing/extravagant}
echo "Substitution: $modified"
# Output: Substitution: Linux is extravagant!

# Extracting a substring
substring=${original:7:9}  # Extracts "is amazing"
echo "Substring: $substring"
# Output: Substring: is amazing

# Checking the length of the string
length=${#original}
echo "Length of the string: $length characters"
# Output: Length of the string: 20 characters

Shell Associative Arrays #

#!/bin/bash

# Declare an associative array
declare -A user_info

# Assign key-value pairs
user_info[name]="Alice"
user_info[email]="alice@example.com"
user_info[role]="Admin"

# Access elements by key
echo "User Name: ${user_info[name]}"
echo "User Email: ${user_info[email]}"
echo "User Role: ${user_info[role]}"

# Looping over keys and values
for key in "${!user_info[@]}"; do
    echo "$key: ${user_info[$key]}"
done

YAML files #

Install yq

sudo pacman -S yq

Create yaml example

apiVersion: v1
kind: ComplexConfig
metadata:
  name: example-config
  labels:
    environment: production
    version: "1.0"
spec:
  services:
    - name: service1
      type: LoadBalancer
      ports:
        - name: http
          port: 80
          targetPort: 8080
        - name: https
          port: 443
          targetPort: 8443
      hosts:
        - host: "service1.example.com"
          ip: "192.168.1.10"
          regions:
            - us-east-1
            - eu-west-1
    - name: service2
      type: ClusterIP
      ports:
        - name: grpc
          port: 50051
          targetPort: 50051
      hosts:
        - host: "service2.example.com"
          ip: "192.168.1.20"
          regions:
            - ap-south-1
            - eu-central-1
  config:
    retries: 3
    timeout: 5000
  logging:
    level: debug
    format: json
    outputs:
      - type: file
        path: "/var/log/app.log"
      - type: stdout

filtering data

yq '.metadata.name' config.yaml
yq '.spec.services[].name' config.yaml
yq '.spec.services[1].type' config.yaml
yq '.spec.services[] | select(.name == "service1") | .ports[] | {port, targetPort}' config.yaml
yq '.spec.services[].hosts[] | {host, ip}' config.yaml
yq '.spec.services[] | select(.name == "service2") | .hosts[].regions' config.yaml
yq '.spec.logging.level' config.yaml

yq -r '.metadata.name' config.yaml
yq -r '.spec.services[].name' config.yaml
yq -r '.spec.services[1].type' config.yaml
yq -r '.spec.services[] | select(.name == "service1") | .ports[] | "\(.port) \(.targetPort)"' config.yaml
yq -r '.spec.services[].hosts[] | "\(.host) \(.ip)"' config.yaml
yq -r '.spec.services[] | select(.name == "service2") | .hosts[].regions[]' config.yaml
yq -r '.spec.logging.level' config.yaml

Check input #

#!/bin/bash
# Check if the argument is a directory.
if [[ ! -d "$1" ]]; then
    echo "Error: $1 is not a directory."
    exit 1
fi

validating file path..

#!/bin/bash

# Check if the user provided a file path as an argument
if [[ -z "$1" ]]; then
    echo "No file path provided. Please enter the file path:"
    read -r file_path
else
    file_path="$1"
fi

# Check if the file exists
if [[ -f "$file_path" ]]; then
    echo "File exists, proceeding with backup."
else
    echo "File does not exist. Please check the path and try again."
    exit 1
fi