Bash Workspace¶
Intro¶
List all the shells that are currently installed on our machine by reading the /etc/shell
file:
In [3]:
cat /etc/shells
# /etc/shells: valid login shells /bin/sh /usr/bin/sh /bin/bash /usr/bin/bash /bin/rbash /usr/bin/rbash /bin/dash /usr/bin/dash
A Jupyter kernel for bash¶
cat dog.png | display
echo "<b>Dog</b>, not a cat." | displayHTML
echo "alert('It is known khaleesi\!');" | displayJS
In [3]:
ls
README.md python-workspace.ipynb run-docker-notebook-d.sh bash-workspace.ipynb run-docker-bash.sh run-docker.sh golang-workspace.ipynb run-docker-d.sh
Special Characters and Operatons¶
In [25]:
specialChars() {
echo ~+ # same as "echo $PWD"
cd ~
echo ~- # same as "echo $OLDPWD"
echo $* # all positional arguments passed to script, function, etc
echo $@ # same as above
: # means no operation (no-op)
# ternary operator in Bash!
variable=$(( 1>2 ? 1:2 ))
echo $variable
# 2
# get last positional parameter
last_arg=${!#}
echo $last_arg
# ./script.sh 1 2 3 4
# 4
# if `username` not set, set default value to output of `whoami` command
echo ${username:-$(whoami)}
# output of `whoami` command
# if variable is set, use alternative value
path=/some/path
path=${path:+/some/alternative/path}
echo $path
# /some/alternative/path
}
specialChars
/root /root 2 /usr/bin/bash root /some/alternative/path
base 64 / hex string¶
In [30]:
# generating all the characters for a valid base 64 / hex string
baseAndHex() {
b64_chars=( {a..z} {A..Z} {0..9} + / = )
echo $b64_chars
echo "${#b64_chars[@]}"
# a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 + / =
hex_chars=( {0..9} {a..f} )
echo $hex_chars
echo "${#hex_chars[@]}"
# 0 1 2 3 4 5 6 7 8 9 a b c d e f
}
baseAndHex
a 65 0 16
checking if parameter is set, else printing error message and exit¶
In [35]:
checkParameter() {
local args=("$@")
echo "${args[0]}"
echo "${args[1]}"
echo "${args[2]}"
: ${1?"usage: `basename $0` <username>"}
: ${2?"usage: `basename $0` <username>"}
}
echo -e "\n..."
checkParameter
echo -e "\n..."
checkParameter "a"
echo -e "\n..."
checkParameter "a" "b"
... bash: 1: usage: bash <username> ... a bash: 2: usage: bash <username> ... a b
anonymous functions¶
In [46]:
# send output of code block to file
{
echo 'listing HOME folder'
ls -l ~
echo 'pinging Amazon.com'
} > results.txt
cat results.txt
echo -e "\n..."
# invoke anonymous function after conditional check
# the "-z" option in bash scripting is a test operator that checks if a string is null.
[ ! -z $HOME ] && {
ls -l ~
echo 'do something else'
}
listing HOME folder total 4 drwxr-xr-x. 4 root root 28 May 8 02:46 go -rw-r--r--. 1 root root 20 May 12 21:04 results.txt pinging Amazon.com ... total 4 drwxr-xr-x. 4 root root 28 May 8 02:46 go -rw-r--r--. 1 root root 142 May 12 21:04 results.txt do something else
string manipulation¶
In [56]:
strManipulation() {
str='cool is Bash - Bash is so cool cool!'
# substitute first occurence of word "cool" with "awesome"
echo ${str/cool/awesome}
# substitute all occurances of word "cool with "awesome"
echo ${str//cool/awesome}
# strip string until first occurence of "cool" from front of string
echo ${str#*cool}
# strip string until last occurance of "cool" from front of string
echo ${str##*cool}
# strip string until first occurence of "cool" from back of string
echo ${str%cool*}
# strip string until last occurence of "cool" from back of string
echo ${str%%cool*}
# if variable prefix (beginning of string) matches "cool"
# replace "cool" with tricky
echo ${str/#cool/tricky}
# if variable suffix (end of string) matches "cool!"
# replace "cool!" with tricky!
echo ${str/%cool\\!/trick!}
}
strManipulation
bash: !/trick!}: event not found awesome is Bash - Bash is so cool cool! awesome is Bash - Bash is so awesome awesome! is Bash - Bash is so cool cool! ! cool is Bash - Bash is so cool tricky is Bash - Bash is so cool cool!
loop using shift¶
In [57]:
# Until loop using shift to parse args passed to script or function
loopShift() {
until [ -z $1 ]
do
echo "arg: $1"
shift # moves all positional parameters to the left
done
}
loopShift 'a' 'b' 'c' 'd' 'e' 'f'
arg: a arg: b arg: c arg: d arg: e arg: f
parse space-separated values¶
In [59]:
# use "set" to parse space-separated values as positional arguments
parse() {
for arg in "Will 31" "Jack 40" "Elizabeth 29"; do
# NOTE: '--' tells Bash that values starting "-" should be treated as
# positional arguments and not options.
set -- $arg
name=$1
age=$2
echo "$name is $age years old"
done
# Will is 31 years old
# Jack is 40 years old
# Elizabeth is 29 years old
}
parse 'a' 'b' 'c' 'd' 'e' 'f'
Will is 31 years old Jack is 40 years old Elizabeth is 29 years old
Handle Errors¶
In [60]:
handleError() {
if cat file; then
echo "Exit code 0. No problems."
else
echo "Exit code $?"
echo "Handling error.."
fi
}
handleError
cat: file: No such file or directory Exit code 1 Handling error..
Timeout¶
In [67]:
date
timeout 2s sleep 5
date
timeout 2s echo Done
date
timeout 3s bash -c 'echo DONE'
Sun May 12 21:24:38 UTC 2024 Sun May 12 21:24:40 UTC 2024 Done Sun May 12 21:24:40 UTC 2024 DONE
Handle Arguments And Options¶
Sometimes, we want to add arguments to provide to our script variables to make it more generic.
$1
is the first argument$#
variable returns the number of arguments provided to the command line${#args[@]}
returns the number of args array elements
The shift
command is another handy tool for managing arguments in bash scripts. It ‘shifts’ all arguments to the left. For instance, if you use shift, $2
becomes $1
, $3
becomes $2
, and so on. The original first argument is discarded.
One argument¶
In [30]:
# ONE ARGUMENT
oneArgument () {
echo $1
}
echo -e "\nONE ARGUMENT"
oneArgument hello
ONE ARGUMENT hello
Multiple Arguments¶
In [31]:
# MULTIPLE ARGUMENTS
multipleArguments () {
local args=("$@")
if [ ${#args[@]} -ne 2 ]; then
echo "Error: Missing or too many arguments."
exit 1
fi
local helloMessage=(${args[0]})
local name=(${args[1]})
echo $helloMessage $name
}
echo -e "\nMULTIPLE ARGUMENTS"
multipleArguments hi John
MULTIPLE ARGUMENTS hi John
Simple Option¶
In [32]:
# SIMPLE OPTION
simpleOption () {
local args
local short_presentation=false
while [ $# -gt 0 ]; do
case "$1" in
-s|--short-presentation)
short_presentation=true
shift
;;
-*|--*)
echo "Error: Invalid option: $1"
;;
*)
args+=("$1")
shift
;;
esac
done
if [ ${#args[@]} -ne 2 ]; then
echo "Error: Missing or too many arguments."
exit 1
fi
local helloMessage=(${args[0]})
local name=(${args[1]})
if [ "$short_presentation" = true ]; then
echo $helloMessage
else
echo $helloMessage $name
fi
}
echo -e "\nSIMPLE OPTION"
simpleOption hello Anna
simpleOption -s hello Anna
SIMPLE OPTION hello Anna hello
Value Option¶
In [33]:
# VALUE OPTION
valueOption () {
local args
local short_presentation=false
local allowed_colors="green yellow red"
check_option() {
for option in $2; do
if [ "$option" = "$1" ]; then
return 0
fi
done
echo "Error: Invalid option value : $1"
exit 1
}
echo_color () {
case $1 in
green)
echo "\033[0;32m${@:2}\033[0m"
;;
yellow)
echo "\033[0;33m${@:2}\033[0m"
;;
red)
echo "\033[0;31m${@:2}\033[0m"
;;
*)
echo "${@:2}"
;;
esac
}
while [ $# -gt 0 ]; do
case "$1" in
-s|--short-presentation)
short_presentation=true
shift
;;
-c=*|--color=*)
check_option "${1#*=}" "$allowed_colors"
color="${1#*=}"
shift
;;
-*|--*)
echo "Error: Invalid option: $1"
exit 1
;;
*)
args+=("$1")
shift
;;
esac
done
if [ ${#args[@]} -ne 2 ]; then
echo "Error: Missing or too many arguments."
fi
local helloMessage=(${args[0]})
local name=(${args[1]})
if [ "$short_presentation" = true ]; then
echo_color $color $helloMessage
else
echo_color $color $helloMessage $name
fi
}
echo -e "\nVALUE OPTION"
valueOption "hi" "Jhon" --color=red
valueOption "what's up" "John" --color=green
valueOption hello Anna --color=yellow
VALUE OPTION \033[0;31mhi Jhon\033[0m \033[0;32mwhat's John\033[0m \033[0;33mhello Anna\033[0m
Usage¶
In [2]:
usage() {
echo
echo "Usage: $0 [...options] <hello_message> <name>"
echo "Options:"
echo " -h, --help Show this help message."
echo " -c, --color COLOR Set the color for the message (green, yellow or red)."
echo " -s, --short-presentation Display presentation message without name."
echo
echo "Example: $0 -c green 'Hello' 'John'"
echo
}
usage
Usage: /usr/bin/bash [...options] <hello_message> <name> Options: -h, --help Show this help message. -c, --color COLOR Set the color for the message (green, yellow or red). -s, --short-presentation Display presentation message without name. Example: /usr/bin/bash -c green 'Hello' 'John'
In [14]:
# SIMPLE USAGE
simpleUsage () {
local args
local short_presentation=false
local allowed_colors="green yellow red"
usage() {
echo
echo "Usage: $0 [-s|--short-presentation] [-c|--color=<green|yellow|red>] [-h|--help] <hello_message> <name>"
echo
}
check_option() {
for option in $2; do
if [ "$option" = "$1" ]; then
return 0
fi
done
echo "Error: Invalid option value : $1"
usage
exit 1
}
echo_color () {
case $1 in
green)
echo "\033[0;32m${@:2}\033[0m"
;;
yellow)
echo "\033[0;33m${@:2}\033[0m"
;;
red)
echo "\033[0;31m${@:2}\033[0m"
;;
*)
echo "${@:2}"
;;
esac
}
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
usage
exit 1
;;
-s|--short-presentation)
short_presentation=true
shift
;;
-c=*|--color=*)
check_option "${1#*=}" "$allowed_colors"
color="${1#*=}"
shift
;;
-*|--*)
echo "Error: Invalid option: $1"
usage
exit 1
;;
*)
args+=("$1")
shift
;;
esac
done
if [ ${#args[@]} -ne 2 ]; then
echo "Error: Missing or too many arguments."
usage
exit 1
fi
local helloMessage=(${args[0]})
local name=(${args[1]})
if [ "$short_presentation" = true ]; then
echo_color $color $helloMessage
else
echo_color $color $helloMessage $name
fi
}
echo -e "\nSIMPLE USAGE"
# simpleUsage -h
# simpleUsage "what's up" "John" "how's it going"
# simpleUsage hello Anna --color=purple
simpleUsage hello Anna -d
SIMPLE USAGE Error: Invalid option: -d Usage: /usr/bin/bash [-s|--short-presentation] [-c|--color=<green|yellow|red>] [-h|--help] <hello_message> <name> exit Restarting Bash
String Manipulation¶
substring extraction and replacement¶
In [6]:
# substring extraction by providing character position and length
stringExtration() {
str="2023-10-12"
echo "${str:5:2}" # 10
echo "${str::4}" # 2023
echo "${str:5}" # 10-12
}
stringExtration
echo -e "\n..."
# substring from the right side, as folloes
stringExtration() {
str="backup.sql"
echo "original${str:(-4)}" # original.sql
}
stringExtration
echo -e "\n..."
# inbuilt syntax for subtring replacements
stringExtration() {
str="obin-linux_x64-bin"
echo "${str/x64/armhf}" # obin-linux_armhf-bin
echo "${str/bin/dist}" # odist-linux_x64-bin
echo "${str//bin/dist}" # odist-linux_x64-dist
}
stringExtration
echo -e "\n..."
# replace string prefixes and suffixes
# replacing a file extension with another extension is a good example
stringExtration() {
str="db_config_backup.zip"
echo "${str/%.zip/.conf}" # db_config_backup.conf
echo "${str/#db/settings}" # settings_config_backup.zip
# in the above substring replacement,
# it used the exact substring segment for matching
# to use a part of the substring, us the "*" wildcard character
echo "${str/%.*/.bak}" # db_config_backup.bak
echo "${str/#*_/new}" # newbackup.zip
}
stringExtration
10 2023 10-12 ... original.sql ... obin-linux_armhf-bin odist-linux_x64-bin odist-linux_x64-dist ... db_config_backup.conf settings_config_backup.zip db_config_backup.bak newbackup.zip
regex matches, extractions, and replacements¶
Use inbuilt Bash regex features to handle text processing faster than external binaries
In [11]:
# performs a regex match with an if-condition
# and the "=~" operator
regexSample() {
str="db_backup_2003.zip"
if [[ $str =~ 200[0-5]+ ]]; then
echo "regex_matched"
fi
}
regexSample
echo -e "\n..."
# replace the if-statement with an iline conditional
regexSample() {
[[ $str =~ 200[0-5]+ ]] && echo "regex_matched"
}
regexSample
echo -e "\n..."
# once the Bash interpreter performs a regex match,
# it typically stores all matches in the "BASH_REMATCH" shell variable.
# this variable is a read-only array, and
# it stores the entire matched data in the first index.
# IF using sub-patterns, Bash incrementally keeps those matches in other indexes.
regexSample() {
str="db_backup_2003.zip"
if [[ $str =~ (200[0-5])(.*$) ]]; then
echo "${BASH_REMATCH[0]}" # 2003.zip
echo "${BASH_REMATCH[1]}" # 2003
echo "${BASH_REMATCH[2]}" # .zip
fi
}
regexSample
echo -e "\n..."
# it's possible to use regex definitions inside parameter expansions
regexSample() {
str="db_backup_2003.zip"
re="200[0-3].zip"
echo "${str/$re/new}.bak" # db_backup_new.bak
}
regexSample
regex_matched ... regex_matched ... 2003.zip 2003 .zip ... db_backup_new.bak
substring removal techniques¶
In [16]:
# use the substring replacement syntax
# but omit the replacement string parameter for string removals
removal_technique() {
str="ver5.02-2224.e2"
ver="${str#ver}"
echo $ver # 5.02-2224.e2
maj="${ver/.*}"
echo $maj # 5
# in the above, it used the exact substring
# and a wildcard for substring removal.
# check below how to extract a clean version number without excessive characters
str="ver5.02-2224_release"
ver="${str//[a-z_]}"
echo $ver # 5.02-2224
}
removal_technique
5.02-2224.e2 5 5.02-2224 ...
case conversions and case-based variables¶
In [19]:
caseString() {
str="Hello Bash!"
lower="${str,,}"
upper="${str^^}"
echo $lower # hello bash!
echo $upper # HELLO BASH!
# uppercase or lowercase only the first character of a particular string
ver1="V2.0-release"
ver2="v4.0-release"
echo "${ver1,}" # v2.0-release
echo "${ver2^}" # V4.0-release
# if need to make a specific variable strictly uppercase or lowercase,
# add case attributes to a particular variable with the inbuilt "declare" command
declare -l ver1
declare -u ver2
ver1="V4.02.2"
ver2="v2.22.1"
echo $ver1 # v4.02.2
echo $ver2 # V2.22.1
# the above "ver1" and "ver2" variables receive a case attribute during the declaration,
# so whenever you assign a value for a specific variable,
# Bash converts the text case based on variable attributes.
}
caseString
hello bash! HELLO BASH! v2.0-release V4.0-release v4.02.2 V2.22.1
splitting strings (string-to-array conversion)¶
In [22]:
# using "IFS" and "read" is one of the simplest and error-free ways to split a string
splitting() {
str="C,C++,JavaScript,Python,Bash"
IFS="," read -ra arr <<< "$str"
echo "${#arr[@]}" # 5
echo "${arr[0]}" # C
echo "${arr[4]}" # Bash
# the above uses "," as the split delimiter
# and uses the "read" inbuilt command to create an array based on "IFS"
# BUT it breaks when including "*"
# (expands to current directory's content)
# as an element and space as the delimiter
# WARNING: This code has several hidden issues.
str="C,Bash,*"
arr=(${str//,/ })
echo "..."
echo "${#arr[@]}" #
}
splitting
5 C Bash ... 7