Last active
July 13, 2024 22:54
-
-
Save wcarhart/23008155c0699b497879595c84294296 to your computer and use it in GitHub Desktop.
Helpful Bash design patterns
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Check file/directory existence ========== | |
if [[ -f $thing ]] ; then | |
echo "$thing is a file" | |
elif [[ -d $thing ]] ; then | |
echo "$thing is a directory" | |
elif [[ -s $thing ]] ; then | |
echo "$thing is a nonempty file" | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Check filetype ========== | |
function check_filetype { | |
# $1 is the filename | |
# $2 is the desired filetype | |
if [[ "$1" == *$2 ]] ; then | |
return 1 | |
fi | |
return 0 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Build a CLI parser ========== | |
if [[ $# -eq 0 ]] ; then | |
usage | |
exit 1 | |
fi | |
while [[ $# -gt 0 ]] ; do | |
key="$1" | |
case "$key" in | |
# short option | |
-a ) | |
# do something | |
shift | |
;; | |
# long option | |
-b|--bb ) | |
# do something | |
shift | |
;; | |
# argument | |
-c|--arg ) | |
# consume the argument | |
parse_arg "$2" | |
shift 2 | |
;; | |
# help menu | |
-h|--help ) | |
usage | |
exit 0 | |
;; | |
# handle default | |
* ) | |
>&2 echo "-err: unknown option '$key'" | |
exit 1 | |
;; | |
esac | |
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Copy to clipboard if on MacOS ========== | |
if [[ `uname -s` == *Darwin* ]] ; then | |
echo -n "$content" | pbcopy | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Run a detached command without an attached terminal ========== | |
# can be useful over SSH | |
cmd="echo 'hello world'" | |
echo "$cmd &" | script 'screen -' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Allow wildcards to return empty lists ========== | |
shopt -s nullglob | |
shopt -s dotglob | |
files=( /dir0/dir1/* ) | |
shopt -u nullglob | |
shopt -u dotglob |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Exit on errors ========== | |
set -e | |
some_risky_command | |
set +e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Array manipulation ========== | |
arr=( ) | |
for item in "${arr[@]}" ; do ... ; done | |
len="${#arr[@]}" | |
arr2=( "abc" "def" "ghi" ) | |
arr3=( "jkl" "mno" "pqr" ) | |
arr=( "${arr2[@]}" "${arr3[@]}" ) | |
for ((i = 0; i< ${#arr[@]}; i++)) ; do item="${arr[$i]}" ; ... ; done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Helpful shortcuts ========== | |
$$ # PID of the shell, or PID of the invoking shell (if in a subshell) | |
$? # exit code of previous command | |
$@ # expands all arguments into an array | |
$* # expands all arguments into a string | |
$# # number of arguments | |
$- # current option flags | |
$! # PID of process most recently placed in the background | |
$_ # at shell startup, absolute path to shell; subsequently, the last argument of the last command | |
$0 # name of the current script or current shell | |
$1 # 1st argument | |
$2 # 2nd argument | |
!! # previous command | |
!$ # last argument of previous command | |
!^ # first argument of previous command | |
!:2 # second argument of previous command | |
!:3 # third argument of previous command |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Helpful string manipulation ========== | |
test_string="filename.json.gz" | |
# get prefix with '%' | |
echo "${test_string%.*}" # filename.json | |
echo "${test_string%%.*}" # filename | |
# get suffix with '#' | |
echo "${test_string#*.}" # json.gz | |
echo "${test_string##*.}" # gz |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Modular script template ========== | |
#!/bin/bash | |
set -o errexit -o nounset -o pipefail | |
function main { | |
: Provide default command logic. ; | |
# parse args | |
# verify something | |
# run main logic | |
} | |
# utilities | |
function msg { out "$*" >&2 ;} | |
function err { local x=$? ; msg "$*" ; return $(( $x == 0 ? 1 : $x )) ;} | |
function out { printf '%s\n' "$*" ;} | |
# handles "no-match" exit code specified by POSIX for filtering tools. | |
function maybe { "$@" || return $(( $? == 1 ? 0 : $? )) ;} | |
# delegates to subcommands or runs main, as appropriate | |
if declare -F -- "${1:-}" >/dev/null ; then | |
"$@" | |
else | |
main | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Redirect multiline string to file ========== | |
cat << EndOfString >> myfile.txt | |
line 0 | |
line 1 | |
line 2 | |
EndOfString |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Build a script that uses a set of subcommands as functions ========== | |
function f1 { | |
if [[ "$1" == "--help" || "$1" == "-h" ]] ; then | |
echo -e ">> scriptname.sh f1\nDescription of function f1" | fold -w 100 -s | |
return | |
fi | |
# do something in f1 | |
} | |
function f2 { | |
if [[ "$1" == "--help" || "$1" == "-h" ]] ; then | |
echo -e ">> scriptname.sh f2\nDescription of function f2" | fold -w 100 -s | |
return | |
fi | |
# do something in f2 | |
} | |
function help { | |
if [[ "$1" == "--help" || "$1" == "-h" ]] ; then | |
echo -e ">> help\nShow this menu and exit" | fold -w 100 -s | |
return | |
fi | |
cat << EndOfHelp | |
Script description | |
Usage: | |
scriptname.sh COMMAND | |
Available commands: | |
`declare -F | awk '{print $NF}' | sed "s/^/ /"` | |
$( \ | |
for cmd in $(declare -F | awk '{print $NF}') ; do \ | |
echo "$(/path/to/scriptname.sh $cmd --help)" ; \ | |
echo ; \ | |
done \ | |
) | |
EndOfHelp | |
} | |
if declare -F -- "${1:-}" >/dev/null ; then | |
"$@" | |
else | |
>&2 echo "-err: no such command '$1'" | |
>&2 echo "Use 'help' for available commands" | |
exit 1 | |
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Create tab autocomplete ========== | |
# your script will need a `list` option that lists all subcommands/options | |
function _autocomplete { | |
local cur prev opts | |
COMPREPLY=( ) | |
cur="${COMP_WORDS[COMP_CWORD]}" | |
prev="${COMP_WORDS[COMP_CWORD-1]}" | |
opts="$(/path/to/scriptname.sh list | tr '\n' ' ')" | |
# if you want to autocomplete flags (options starting with '-') | |
if [[ ${cur} == -* ]] ; then | |
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |
return 0 | |
fi | |
# if you want to autocomplete subcommands (options not starting with '-') | |
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |
return 0 | |
} | |
complete -F _autocomplete /path/to/scriptname.sh | |
# if you alias your script, like so: | |
alias myscript='/path/to/scriptname.sh' | |
# then you can tell `complete` to use the alias instead of the file: | |
complete -F _autocomplete myscript |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Make a usage function ========== | |
function usage { | |
cat << EndOfUsage | |
Program description | |
Usage: | |
my_program.sh [-h] [ARGS] | |
Required arguments: | |
... | |
Optional arguments: | |
-h, --help show this help menu and exit | |
... | |
EndOfUsage | |
} | |
# you can also use <<- with HEREDOCs to avoid weird indentation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Set up variables from arguments ========== | |
function validate_arg { | |
# $1 is the variable name | |
# $2 is the variable content | |
# perform validations on $2, return 1 if they fail | |
# then, read into variable | |
read $1 <<< "$2" | |
# if variable content is a path, you can make it absolute to be safe: | |
read $1 <<< "`readlink -m $2`" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ========== Verify files in a list of directories ========== | |
shopt -s nullglob | |
for res in "${resources[@]}" ; do | |
files=( ${!res}* ) | |
# verify files |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Wow, this is exactly what I needed all along. Thank you for creating this gist!