Essential shell tips for Linux enthusiasts
saving your time: some tips for linux shell
Index #
Looping: command vs 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..
- Cleaning Temporary Files
- Graceful Interruption of Services
- Avoiding Database Corruption
- Releasing Network or Hardware Resources
- Maintaining Consistent Variable State-
Signal | Description | How to Invoke |
---|---|---|
SIGINT | Interrupts the process. Typically generated by pressing Ctrl+C in the terminal. | Ctrl + C or kill -2 <pid> |
SIGTERM | Requests the graceful termination of the process. Used to terminate processes without forcing them. | kill -15 <pid> |
SIGHUP | Indicates that the terminal connection was lost or that the process needs to reload configurations. | kill -1 <pid> |
SIGKILL | Forces the immediate termination of a process. Cannot be ignored by the process. | kill -9 <pid> |
SIGSTOP | Pauses the execution of a process (can be resumed later). | kill -19 <pid> |
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
SIGHUP #
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
shellcheck #
shellcheck is a powerful static analysis tool for shell scripts. It helps identify common issues like syntax errors, unused variables, and unsafe practices. Using shellcheck can save time and prevent bugs in your scripts.
shellcheck example.sh
Output from shellcheck
example.sh:5:7: note: Double quote to prevent globbing and word splitting. [SC2086]
example.sh:7:7: warning: Undefined variable: UNDEFINED_VAR. [SC2154]
example.sh:10:7: error: Missing 'fi' to end 'if' statement. [SC1073]