Skip to content

Instantly share code, notes, and snippets.

@Segmentational
Last active June 29, 2025 09:34
Show Gist options
  • Save Segmentational/7866854b34770c92b4fcc5abc01c3053 to your computer and use it in GitHub Desktop.
Save Segmentational/7866854b34770c92b4fcc5abc01c3053 to your computer and use it in GitHub Desktop.
Bash-Example.Bash
#!/usr/bin/env bash
# -*- Coding: UTF-8 -*- #
# -*- System: Linux -*- #
# -*- Usage: *.* -*- #
#
# Advanced Bash Example
#
# Script makes use of the following concepts:
# - shellcheck
# - setopt (set-options)
# - dictionaries/hashmaps
# - bash builtins
# - standard-output & standard-error redirects
# - jq projections
# - coproc
#
# Shellcheck Ignore List
#
# shellcheck disable=SC1073
# shellcheck disable=SC2120
# shellcheck disable=SC2071
# shellcheck disable=SC2086
#
#
# See Bash Set-Options Reference Below
#
set -o pipefail
set -o pipeline
set -o errexit
set -o xtrace
function unset-aws-environment-variables() {
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
unset AWS_PROFILE
unset AWS_DEFAULT_PROFILE
}
function validate() {
[[ (( ${#FUNCNAME[*]} < 2 )) ]] && echo -e "Fatal Error - Cannot Call \"${FUNCNAME[0]}\" || \"${FUNCNAME[1]}\" From Global Namespace." > /dev/stderr
[[ (( ${#FUNCNAME[*]} < 2 )) ]] && return 1
echo "[Debug] (${FUNCNAME[1]}) Evaluating Input. Arguments-Count: (${#}), Function-Count: (${#FUNCNAME[*]})" > /dev/stderr
if ! [[ ${#} -eq 2 ]]; then
echo "[Error] (${FUNCNAME[0]}) Invalid Arguments Received. Caller: \"${FUNCNAME[1]}\". Expected Total Arguments Required (1), and Total Caller Arguments (2)." > /dev/stderr
return 1
fi
return 0
}
function generate-versioned-object-delete-input() {
validate 1 ${#} || echo "[Error] (${FUNCNAME[0]}) Invalid Arguments Received. Expected Bucket-Name (1)." > /dev/stderr
[[ -z "${1}" ]] && echo "[Error] (${FUNCNAME[0]}) Invalid Bucket-Name (1). Function: \"${FUNCNAME[0]}\", Caller: \"${FUNCNAME[1]}\"" > /dev/stderr
[[ -z "${1}" ]] && return 1
declare bucket
declare input
declare output
readonly bucket="${1}"
readonly input="input.delete-objects.${bucket}.json"
readonly output="output.delete-objects.${bucket}.json"
jq --indent 4 ". += {Quiet: false}" > "${output}" <(aws s3api list-object-versions --bucket "${bucket}" --output "json" --query "{Objects: Versions[].{Key:Key,VersionId:VersionId}}")
echo "[Debug] (${FUNCNAME[0]}) Generated Object-Delete-Input. Format: JSON, File: \"${input}\"" > /dev/stderr
printf "%s" "${output}" > /dev/stdout
return 0
}
function delete-versioned-object() {
validate 2 ${#} || echo "[Error] (${FUNCNAME[0]}) Invalid Arguments Received. Expected Bucket-Name (1), Delete-Object-Input-File (2)." > /dev/stderr
[[ -z "${1}" ]] && echo "[Error] (${FUNCNAME[0]}) Invalid Bucket-Name (1). Caller: \"${FUNCNAME[1]}\"" > /dev/stderr
[[ -z "${1}" ]] && return 1
[[ -z "${2}" ]] && echo "[Error] (${FUNCNAME[0]}) Invalid Delete-Object-Input-File (2). Caller: \"${FUNCNAME[1]}\"" > /dev/stderr
[[ -z "${2}" ]] && return 1
declare bucket
declare file
readonly bucket="${1}"
readonly file="${2}"
echo "[Debug] (${FUNCNAME[0]}) Deleting AWS S3 Versioned Objects. File: \"${file}\"" > /dev/stderr
aws s3api delete-objects --bucket "${bucket}" --delete "file://${file}"
echo "[Log] (${FUNCNAME[0]}) Successfully Deleted All Versioned Objects" > /dev/stderr
return 0
}
function delete-s3-bucket() {
validate 1 ${#} || echo "[Error] (${FUNCNAME[0]}) Invalid Arguments Received. Expected Bucket-Name (1)." > /dev/stderr
[[ -z "${1}" ]] && echo "[Error] (${FUNCNAME[0]}) Invalid Bucket-Name (1). Caller: \"${FUNCNAME[1]}\"" > /dev/stderr
[[ -z "${1}" ]] && return 1
declare bucket
readonly bucket="${1}"
echo "[Debug] (${FUNCNAME[0]}) Deleting AWS S3 Bucket. Bucket-Name: \"${bucket}\"" > /dev/stderr
aws s3 rb "${bucket}" --force
echo "[Log] (${FUNCNAME[0]}) Successfully Deleted All Versioned Objects" > /dev/stderr
return 0
}
function worker() {
validate 1 ${#} || echo "[Error] (${FUNCNAME[0]}) Invalid Arguments Received. Expected Worker-Name (1)." > /dev/stderr
[[ -z "${1}" ]] && echo "[Error] (${FUNCNAME[0]}) Invalid Worker-Name (1). Caller: \"${FUNCNAME[1]}\"" > /dev/stderr
[[ -z "${1}" ]] && return 1
declare name
readonly name="${1}"
# Start a named coprocess "process-${1}" running the "sleep" command following "echo".
coproc "process-${1}" { sleep 5; echo "Complete"; return 0 }
}
function pre-flight-checks() {
declare name
name="jq"
if [[ -z $(command -v "${name}") ]]; then
echo "[Error] (${FUNCNAME[0]}) Command Not Found. Target: \"${name}\"" > /dev/stderr
return 1
fi
name="aws"
if [[ -z $(command -v "${name}") ]]; then
echo "[Error] (${FUNCNAME[0]}) Command Not Found. Target: \"${name}\"" > /dev/stderr
return 1
fi
}
function main() {
echo "[Log] (${FUNCNAME[0]}) Deleting (${#*}) Total Bucket(s)" > /dev/stderr
for item in ${*}; do
declare -A mapping=( ["bucket-name"]="${item}" )
echo "[Debug] (${FUNCNAME[0]}) Evaluating. Bucket-Name: \"${mapping["bucket-name"]}\"" > /dev/stderr
mapping["object-delete-input"]="$(generate-versioned-object-delete-input "${mapping["bucket-name"]}")"
delete-versioned-object "${mapping["bucket-name"]}" "${mapping["object-delete-input"]}"
done
}
pre-flight-checks
declare -a buckets=(
"example-bucket-1"
"example-bucket-2"
"example-bucket-3"
)
main "${buckets[@]}"
# 1. set -o verbose ::: Print shell input upon read.
# 2. set -o allexport ::: Export all variable(s) + function(s) to environment.
# 3. set -o errexit ::: Exit immediately upon pipeline'd failure.
# 4. set -o monitor ::: Output process-separated command(s).
# 5. set -o privileged ::: Ignore externals - ensures of pristine run environment.
# 6. set -o xtrace ::: Print a trace of simple commands.
# 7. set -o braceexpand ::: Enable brace expansion. Enabled by default.
# 8. set -o no-exec ::: Bash syntax debugging; reads in commands but does not execute them.
# 9. set -o pipefail ::: Use the return of the first failed, right-most command. Zero if all successful.
# 10. set -o history ::: Enable the use of history for the given script.
# 11. set -o pipeline ::: Ensures the pipeline return value is that of the last command to exit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment