Created
May 3, 2021 11:19
-
-
Save vladshut/a80e840064b0813bdf52d1b28e1cfc14 to your computer and use it in GitHub Desktop.
Revisions
-
vladshut created this gist
May 3, 2021 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,874 @@ #!/usr/bin/env bash ### ============================================================================== ### SO HOW DO YOU PROCEED WITH YOUR SCRIPT? ### 1. define the options/parameters and defaults you need in list_options() ### 2. define dependencies on other programs/scripts in list_dependencies() ### 3. implement the different actions in main() with helper functions ### 4. implement helper functions you defined in previous step ### ============================================================================== ### Created by vs ( vs ) on 2021-05-03 ### Based on https://github.com/pforret/bashew 1.16.1 script_version="0.0.1" # if there is a VERSION.md in this script's folder, it will take priority for version number readonly script_author="fred.shut.vlad@gmail.com" readonly script_created="2021-05-03" readonly run_as_root=-1 # run_as_root: 0 = don't check anything / 1 = script MUST run as root / -1 = script MAY NOT run as root list_options() { ### Change the next lines to reflect which flags/options/parameters you need ### flag: switch a flag 'on' / no value specified ### flag|<short>|<long>|<description> ### e.g. "-v" or "--verbose" for verbose output / default is always 'off' ### will be available as $<long> in the script e.g. $verbose ### option: set an option / 1 value specified ### option|<short>|<long>|<description>|<default> ### e.g. "-e <extension>" or "--extension <extension>" for a file extension ### will be available a $<long> in the script e.g. $extension ### list: add an list/array item / 1 value specified ### list|<short>|<long>|<description>| (default is ignored) ### e.g. "-u <user1> -u <user2>" or "--user <user1> --user <user2>" ### will be available a $<long> array in the script e.g. ${user[@]} ### param: comes after the options ### param|<type>|<long>|<description> ### <type> = 1 for single parameters - e.g. param|1|output expects 1 parameter <output> ### <type> = ? for optional parameters - e.g. param|1|output expects 1 parameter <output> ### <type> = n for list parameter - e.g. param|n|inputs expects <input1> <input2> ... <input99> ### will be available as $<long> in the script after option/param parsing echo -n " #commented lines will be filtered flag|h|help|show usage flag|q|quiet|no output flag|v|verbose|output more flag|f|force|do not ask for confirmation (always yes) flag|r|remove|remove original option|s|size|size of the output image (default is the smae as original) option|bs|bordersize|size of the border|0 option|c|color|color for the border |white option|l|log_dir|folder for log files |$HOME/log/$script_prefix option|t|tmp_dir|folder for temp files|/tmp/$script_prefix option|o|output|out file template (:ofolder: - original file folder, :oname: - original file name, :oext: - original file extension)|:ofolder:/:oname:_squared.:oext: param|1|input|input files (example: test/*.jpg) " | grep -v '^#' | grep -v '^\s*$' } ##################################################################### ## Put your main script here ##################################################################### main() { log_to_file "[$script_basename] $script_version started" action="modify_images" action=$(lower_case "$action") case $action in check|env) ## leave this default action, it will make it easier to test your script #TIP: use «$script_prefix check» to check if this script is ready to execute and what values the options/flags are #TIP:> $script_prefix check #TIP: use «$script_prefix env» to generate an example .env file #TIP:> $script_prefix env > .env check_script_settings ;; update) ## leave this default action, it will make it easier to test your script #TIP: use «$script_prefix update» to update to the latest version #TIP:> $script_prefix check update_script_to_latest ;; *) modify_images ;; esac log_to_file "[$script_basename] ended after $SECONDS secs" #TIP: >>> bash script created with «pforret/bashew» #TIP: >>> for bash development, also check out «pforret/setver» and «pforret/progressbar» } ##################################################################### ## Put your helper scripts here ##################################################################### modify_images() { log_to_file "modify_images [$input]" require_binary "convert" folder=$(pwd) i=1 for img in $input ; do outputfile=$(outputfile $img) squareup $img $outputfile add_border $outputfile $outputfile echo "${i} ${img} -> ${outputfile}" if flag_set "remove" then echo "Remove original $img" rm "$img" fi i=$(( i+1 )) done } outputfile() { local infile=$1 inputfilename=$(basename "$infile") ofolder=$(dirname "$infile") oname=${inputfilename%%.*} oext=${inputfilename##*.} outputfile=${output//":ofolder:"/$ofolder} outputfile=${outputfile//":oname:"/$oname} outputfile=${outputfile//":oext:"/$oext} echo "$outputfile" } squareup() { # setup temporary images and auto delete upon exit # use mpc/cache to hold input image temporarily in memory local infile=$1 local outfile=$2 tmpA="$tmp_dir/squareup_$$.mpc" tmpB="$tmp_dir/squareup_$$.cache" trap "rm -f $tmpA $tmpB;" 0 trap "rm -f $tmpA $tmpB; exit 1" 1 2 3 15 trap "rm -f $tmpA $tmpB; exit 1" ERR # test if infile exists and compute dimensions if convert -quiet "$infile" +repage "$tmpA" then width=`identify -format "%w" $tmpA` height=`identify -format "%h" $tmpA` else out "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" fi # compute max and min dimensions if [ $width -gt $height ] then max=$width min=$height else max=$height min=$width fi if [ "$size" = "" ] then dim=$max else # get % value if specified from size factor=`echo "$size" | sed -n 's/\([.0-9]*\)%$/\1/ p'` if [ "$factor" = "" ] then # test if contains a period factor2=`echo "$size" | sed -n 's/\([0-9]*[.][0-9]*\)$/\1/ p'` [ "$factor2" != "" ] && errMsg "SIZE=$size IS NOT AN INTEGER" fi # compute pixel equivalent desired size if [ "$factor" = "" ] then dim=$size [ $width -gt $height ] && scale="${dim}x" || scale="x${dim}" else dim=`echo "scale=0; $factor * $max / 100" | bc` [ $width -gt $height ] && scale="${dim}x" || scale="x${dim}" fi fi # convert trans to none for bgcolor so that background is transparent [ "$color" = "trans" ] && color="none" # add resize if size != "" [ "$size" = "" ] && resize="" || resize="-filter $filter -resize $scale" # process image convert \( -size ${dim}x${dim} xc:$color \) \( $tmpA $resize \) -gravity "Center" -composite +repage "$outfile" } add_border() { local infile=$1 local outfile=$2 # process image convert "$infile" -bordercolor "$color" -border "$bordersize" "$outfile" } ##################################################################### ################### DO NOT MODIFY BELOW THIS LINE ################### ##################################################################### # set strict mode - via http://redsymbol.net/articles/unofficial-bash-strict-mode/ # removed -e because it made basic [[ testing ]] difficult set -uo pipefail IFS=$'\n\t' hash() { length=${1:-6} if [[ -n $(command -v md5sum) ]]; then # regular linux md5sum | cut -c1-"$length" else # macos md5 | cut -c1-"$length" fi } force=0 help=0 verbose=0 #to enable verbose even before option parsing [[ $# -gt 0 ]] && [[ $1 == "-v" ]] && verbose=1 quiet=0 #to enable quiet even before option parsing [[ $# -gt 0 ]] && [[ $1 == "-q" ]] && quiet=1 ### stdout/stderr output initialise_output() { [[ "${BASH_SOURCE[0]:-}" != "${0}" ]] && sourced=1 || sourced=0 [[ -t 1 ]] && piped=0 || piped=1 # detect if output is piped if [[ $piped -eq 0 ]]; then col_reset="\033[0m" col_red="\033[1;31m" col_grn="\033[1;32m" col_ylw="\033[1;33m" else col_reset="" col_red="" col_grn="" col_ylw="" fi [[ $(echo -e '\xe2\x82\xac') == '€' ]] && unicode=1 || unicode=0 # detect if unicode is supported if [[ $unicode -gt 0 ]]; then char_succ="✅" char_fail="⛔" char_alrt="✴️" char_wait="⏳" info_icon="🌼" config_icon="🌱" clean_icon="🧽" require_icon="🔌" else char_succ="OK " char_fail="!! " char_alrt="?? " char_wait="..." info_icon="(i)" config_icon="[c]" clean_icon="[c]" require_icon="[r]" fi error_prefix="${col_red}>${col_reset}" } out() { ((quiet)) && true || printf '%b\n' "$*"; } debug() { if ((verbose)); then out "${col_ylw}# $* ${col_reset}" >&2; else true; fi; } die() { out "${col_red}${char_fail} $script_basename${col_reset}: $*" >&2 ; tput bel ; safe_exit ; } alert() { out "${col_red}${char_alrt}${col_reset}: $*" >&2 ; } success() { out "${col_grn}${char_succ}${col_reset} $*"; } announce() { out "${col_grn}${char_wait}${col_reset} $*"; sleep 1 ; } progress() { ((quiet)) || ( local screen_width screen_width=$(tput cols 2>/dev/null || echo 80) local rest_of_line rest_of_line=$((screen_width - 5)) if flag_set ${piped:-0}; then out "$*" >&2 else printf "... %-${rest_of_line}b\r" "$* " >&2 fi ) } log_to_file() { [[ -n ${log_file:-} ]] && echo "$(date '+%H:%M:%S') | $*" >>"$log_file"; } ### string processing lower_case() { echo "$*" | tr '[:upper:]' '[:lower:]'; } upper_case() { echo "$*" | tr '[:lower:]' '[:upper:]'; } slugify() { # slugify <input> <separator> # slugify "Jack, Jill & Clémence LTD" => jack-jill-clemence-ltd # slugify "Jack, Jill & Clémence LTD" "_" => jack_jill_clemence_ltd separator="${2:-}" [[ -z "$separator" ]] && separator="-" # shellcheck disable=SC2020 echo "$1" | tr '[:upper:]' '[:lower:]' | tr 'àáâäæãåāçćčèéêëēėęîïííīįìłñńôöòóœøōõßśšûüùúūÿžźż' 'aaaaaaaaccceeeeeeeiiiiiiilnnoooooooosssuuuuuyzzz' | awk '{ gsub(/[\[\]@#$%^&*;,.:()<>!?\/+=_]/," ",$0); gsub(/^ */,"",$0); gsub(/ *$/,"",$0); gsub(/ */,"-",$0); gsub(/[^a-z0-9\-]/,""); print; }' | sed "s/-/$separator/g" } title_case() { # title_case <input> <separator> # title_case "Jack, Jill & Clémence LTD" => JackJillClemenceLtd # title_case "Jack, Jill & Clémence LTD" "_" => Jack_Jill_Clemence_Ltd separator="${2:-}" # shellcheck disable=SC2020 echo "$1" | tr '[:upper:]' '[:lower:]' | tr 'àáâäæãåāçćčèéêëēėęîïííīįìłñńôöòóœøōõßśšûüùúūÿžźż' 'aaaaaaaaccceeeeeeeiiiiiiilnnoooooooosssuuuuuyzzz' | awk '{ gsub(/[\[\]@#$%^&*;,.:()<>!?\/+=_-]/," ",$0); print $0; }' | awk '{ for (i=1; i<=NF; ++i) { $i = toupper(substr($i,1,1)) tolower(substr($i,2)) }; print $0; }' | sed "s/ /$separator/g" | cut -c1-50 } ### interactive confirm() { # $1 = question flag_set $force && return 0 read -r -p "$1 [y/N] " -n 1 echo " " [[ $REPLY =~ ^[Yy]$ ]] } ask() { # $1 = variable name # $2 = question # $3 = default value # not using read -i because that doesn't work on MacOS local ANSWER read -r -p "$2 ($3) > " ANSWER if [[ -z "$ANSWER" ]]; then eval "$1=\"$3\"" else eval "$1=\"$ANSWER\"" fi } trap "die \"ERROR \$? after \$SECONDS seconds \n\ \${error_prefix} last command : '\$BASH_COMMAND' \" \ \$(< \$script_install_path awk -v lineno=\$LINENO \ 'NR == lineno {print \"\${error_prefix} from line \" lineno \" : \" \$0}')" INT TERM EXIT # cf https://askubuntu.com/questions/513932/what-is-the-bash-command-variable-good-for safe_exit() { [[ -n "${tmp_file:-}" ]] && [[ -f "$tmp_file" ]] && rm "$tmp_file" trap - INT TERM EXIT debug "$script_basename finished after $SECONDS seconds" exit 0 } flag_set() { [[ "$1" -gt 0 ]]; } show_usage() { out "Program: ${col_grn}$script_basename $script_version${col_reset} by ${col_ylw}$script_author${col_reset}" out "Updated: ${col_grn}$script_modified${col_reset}" out "Description: Resizes an image and squares it up by padding and adding border." echo -n "Usage: $script_basename" list_options | awk ' BEGIN { FS="|"; OFS=" "; oneline="" ; fulltext="Flags, options and parameters:"} $1 ~ /flag/ { fulltext = fulltext sprintf("\n -%1s|--%-12s: [flag] %s [default: off]",$2,$3,$4) ; oneline = oneline " [-" $2 "]" } $1 ~ /option/ { fulltext = fulltext sprintf("\n -%1s|--%-12s: [option] %s",$2,$3 " <?>",$4) ; if($5!=""){fulltext = fulltext " [default: " $5 "]"; } oneline = oneline " [-" $2 " <" $3 ">]" } $1 ~ /list/ { fulltext = fulltext sprintf("\n -%1s|--%-12s: [list] %s (array)",$2,$3 " <?>",$4) ; fulltext = fulltext " [default empty]"; oneline = oneline " [-" $2 " <" $3 ">]" } $1 ~ /secret/ { fulltext = fulltext sprintf("\n -%1s|--%s <%s>: [secret] %s",$2,$3,"?",$4) ; oneline = oneline " [-" $2 " <" $3 ">]" } $1 ~ /param/ { if($2 == "1"){ fulltext = fulltext sprintf("\n %-17s: [parameter] %s","<"$3">",$4); oneline = oneline " <" $3 ">" } if($2 == "?"){ fulltext = fulltext sprintf("\n %-17s: [parameter] %s (optional)","<"$3">",$4); oneline = oneline " <" $3 "?>" } if($2 == "n"){ fulltext = fulltext sprintf("\n %-17s: [parameters] %s (1 or more)","<"$3">",$4); oneline = oneline " <" $3 " …>" } } END {print oneline; print fulltext} ' } check_last_version(){ ( # shellcheck disable=SC2164 pushd "$script_install_folder" &> /dev/null if [[ -d .git ]] ; then local remote remote="$(git remote -v | grep fetch | awk 'NR == 1 {print $2}')" progress "Check for latest version - $remote" git remote update &> /dev/null if [[ $(git rev-list --count "HEAD...HEAD@{upstream}" 2>/dev/null) -gt 0 ]] ; then out "There is a more recent update of this script - run <<$script_prefix update>> to update" fi fi # shellcheck disable=SC2164 popd &> /dev/null ) } update_script_to_latest(){ # run in background to avoid problems with modifying a running interpreted script ( sleep 1 cd "$script_install_folder" && git pull ) & } show_tips() { ((sourced)) && return 0 # shellcheck disable=SC2016 grep <"${BASH_SOURCE[0]}" -v '$0' \ | awk \ -v green="$col_grn" \ -v yellow="$col_ylw" \ -v reset="$col_reset" \ ' /TIP: / {$1=""; gsub(/«/,green); gsub(/»/,reset); print "*" $0} /TIP:> / {$1=""; print " " yellow $0 reset} ' \ | awk \ -v script_basename="$script_basename" \ -v script_prefix="$script_prefix" \ '{ gsub(/\$script_basename/,script_basename); gsub(/\$script_prefix/,script_prefix); print ; }' } check_script_settings() { if [[ -n $(filter_option_type flag) ]]; then out "## ${col_grn}boolean flags${col_reset}:" filter_option_type flag | while read -r name; do if ((piped)); then eval "echo \"$name=\$${name:-}\"" else eval "echo -n \"$name=\$${name:-} \"" fi done out " " out " " fi if [[ -n $(filter_option_type option) ]]; then out "## ${col_grn}option defaults${col_reset}:" filter_option_type option | while read -r name; do if ((piped)); then eval "echo \"$name=\$${name:-}\"" else eval "echo -n \"$name=\$${name:-} \"" fi done out " " out " " fi if [[ -n $(filter_option_type list) ]]; then out "## ${col_grn}list options${col_reset}:" filter_option_type list | while read -r name; do if ((piped)); then eval "echo \"$name=(\${${name}[@]})\"" else eval "echo -n \"$name=(\${${name}[@]}) \"" fi done out " " out " " fi if [[ -n $(filter_option_type param) ]]; then if ((piped)); then debug "Skip parameters for .env files" else out "## ${col_grn}parameters${col_reset}:" filter_option_type param | while read -r name; do # shellcheck disable=SC2015 ((piped)) && eval "echo \"$name=\\\"\${$name:-}\\\"\"" || eval "echo -n \"$name=\\\"\${$name:-}\\\" \"" done echo " " fi fi } filter_option_type() { list_options | grep "$1|" | cut -d'|' -f3 | sort | grep -v '^\s*$' } init_options() { local init_command init_command=$(list_options | grep -v "verbose|" | awk ' BEGIN { FS="|"; OFS=" ";} $1 ~ /flag/ && $5 == "" {print $3 "=0; "} $1 ~ /flag/ && $5 != "" {print $3 "=\"" $5 "\"; "} $1 ~ /option/ && $5 == "" {print $3 "=\"\"; "} $1 ~ /option/ && $5 != "" {print $3 "=\"" $5 "\"; "} $1 ~ /list/ {print $3 "=(); "} $1 ~ /secret/ {print $3 "=\"\"; "} ') if [[ -n "$init_command" ]]; then eval "$init_command" fi } expects_single_params() { list_options | grep 'param|1|' >/dev/null; } expects_optional_params() { list_options | grep 'param|?|' >/dev/null; } expects_multi_param() { list_options | grep 'param|n|' >/dev/null; } parse_options() { if [[ $# -eq 0 ]]; then show_usage >&2 safe_exit fi ## first process all the -x --xxxx flags and options while true; do # flag <flag> is saved as $flag = 0/1 # option <option> is saved as $option if [[ $# -eq 0 ]]; then ## all parameters processed break fi if [[ ! $1 == -?* ]]; then ## all flags/options processed break fi local save_option save_option=$(list_options | awk -v opt="$1" ' BEGIN { FS="|"; OFS=" ";} $1 ~ /flag/ && "-"$2 == opt {print $3"=1"} $1 ~ /flag/ && "--"$3 == opt {print $3"=1"} $1 ~ /option/ && "-"$2 == opt {print $3"=$2; shift"} $1 ~ /option/ && "--"$3 == opt {print $3"=$2; shift"} $1 ~ /list/ && "-"$2 == opt {print $3"+=($2); shift"} $1 ~ /list/ && "--"$3 == opt {print $3"=($2); shift"} $1 ~ /secret/ && "-"$2 == opt {print $3"=$2; shift #noshow"} $1 ~ /secret/ && "--"$3 == opt {print $3"=$2; shift #noshow"} ') if [[ -n "$save_option" ]]; then if echo "$save_option" | grep shift >>/dev/null; then local save_var save_var=$(echo "$save_option" | cut -d= -f1) debug "$config_icon parameter: ${save_var}=$2" else debug "$config_icon flag: $save_option" fi eval "$save_option" else die "cannot interpret option [$1]" fi shift done ((help)) && ( show_usage check_last_version out " " echo "### TIPS & EXAMPLES" show_tips ) && safe_exit ## then run through the given parameters if expects_single_params; then single_params=$(list_options | grep 'param|1|' | cut -d'|' -f3) list_singles=$(echo "$single_params" | xargs) single_count=$(echo "$single_params" | count_words) debug "$config_icon Expect : $single_count single parameter(s): $list_singles" [[ $# -eq 0 ]] && die "need the parameter(s) [$list_singles]" for param in $single_params; do [[ $# -eq 0 ]] && die "need parameter [$param]" [[ -z "$1" ]] && die "need parameter [$param]" debug "$config_icon Assign : $param=$1" eval "$param=\"$1\"" shift done else debug "$config_icon No single params to process" single_params="" single_count=0 fi if expects_optional_params; then optional_params=$(list_options | grep 'param|?|' | cut -d'|' -f3) optional_count=$(echo "$optional_params" | count_words) debug "$config_icon Expect : $optional_count optional parameter(s): $(echo "$optional_params" | xargs)" for param in $optional_params; do debug "$config_icon Assign : $param=${1:-}" eval "$param=\"${1:-}\"" shift done else debug "$config_icon No optional params to process" optional_params="" optional_count=0 fi if expects_multi_param; then #debug "Process: multi param" multi_count=$(list_options | grep -c 'param|n|') multi_param=$(list_options | grep 'param|n|' | cut -d'|' -f3) debug "$config_icon Expect : $multi_count multi parameter: $multi_param" ((multi_count > 1)) && die "cannot have >1 'multi' parameter: [$multi_param]" ((multi_count > 0)) && [[ $# -eq 0 ]] && die "need the (multi) parameter [$multi_param]" # save the rest of the params in the multi param if [[ -n "$*" ]]; then debug "$config_icon Assign : $multi_param=$*" eval "$multi_param=( $* )" fi else multi_count=0 multi_param="" # [[ $# -gt 0 ]] && die "cannot interpret extra parameters" fi } require_binary(){ binary="$1" path_binary=$(command -v "$binary" 2>/dev/null) [[ -n "$path_binary" ]] && debug "️$require_icon required [$binary] -> $path_binary" && return 0 words=$(echo "${2:-}" | wc -l) if ((force)) ; then announce "Installing $1 ..." case $words in 0) eval "$install_package $1" ;; # require_binary ffmpeg -- binary and package have the same name 1) eval "$install_package $2" ;; # require_binary convert imagemagick -- binary and package have different names *) eval "${2:-}" # require_binary primitive "go get -u github.com/fogleman/primitive" -- non-standard package manager esac else case $words in 0) install_instructions="$install_package $1" ;; 1) install_instructions="$install_package $2" ;; *) install_instructions="${2:-}" esac alert "$script_basename needs [$binary] but it cannot be found" alert "1) install package : $install_instructions" alert "2) check path : export PATH=\"[path of your binary]:\$PATH\"" die "Missing program/script [$binary]" fi } folder_prep() { if [[ -n "$1" ]]; then local folder="$1" local max_days=${2:-365} if [[ ! -d "$folder" ]]; then debug "$clean_icon Create folder : [$folder]" mkdir -p "$folder" else debug "$clean_icon Cleanup folder: [$folder] - delete files older than $max_days day(s)" find "$folder" -mtime "+$max_days" -type f -exec rm {} \; fi fi } count_words() { wc -w | awk '{ gsub(/ /,""); print}'; } recursive_readlink() { [[ ! -L "$1" ]] && echo "$1" && return 0 local file_folder local link_folder local link_name file_folder="$(dirname "$1")" # resolve relative to absolute path [[ "$file_folder" != /* ]] && link_folder="$(cd -P "$file_folder" &>/dev/null && pwd)" local symlink symlink=$(readlink "$1") link_folder=$(dirname "$symlink") link_name=$(basename "$symlink") [[ -z "$link_folder" ]] && link_folder="$file_folder" [[ "$link_folder" == \.* ]] && link_folder="$(cd -P "$file_folder" && cd -P "$link_folder" &>/dev/null && pwd)" debug "$info_icon Symbolic ln: $1 -> [$symlink]" recursive_readlink "$link_folder/$link_name" } lookup_script_data() { readonly script_prefix=$(basename "${BASH_SOURCE[0]}" .sh) readonly script_basename=$(basename "${BASH_SOURCE[0]}") readonly execution_day=$(date "+%Y-%m-%d") #readonly execution_year=$(date "+%Y") script_install_path="${BASH_SOURCE[0]}" debug "$info_icon Script path: $script_install_path" script_install_path=$(recursive_readlink "$script_install_path") debug "$info_icon Linked path: $script_install_path" readonly script_install_folder="$( cd -P "$( dirname "$script_install_path" )" && pwd )" debug "$info_icon In folder : $script_install_folder" if [[ -f "$script_install_path" ]]; then script_hash=$(hash <"$script_install_path" 8) script_lines=$(awk <"$script_install_path" 'END {print NR}') else # can happen when script is sourced by e.g. bash_unit script_hash="?" script_lines="?" fi # get shell/operating system/versions shell_brand="sh" shell_version="?" [[ -n "${ZSH_VERSION:-}" ]] && shell_brand="zsh" && shell_version="$ZSH_VERSION" [[ -n "${BASH_VERSION:-}" ]] && shell_brand="bash" && shell_version="$BASH_VERSION" [[ -n "${FISH_VERSION:-}" ]] && shell_brand="fish" && shell_version="$FISH_VERSION" [[ -n "${KSH_VERSION:-}" ]] && shell_brand="ksh" && shell_version="$KSH_VERSION" debug "$info_icon Shell type : $shell_brand - version $shell_version" readonly os_kernel=$(uname -s) os_version=$(uname -r) os_machine=$(uname -m) install_package="" case "$os_kernel" in CYGWIN* | MSYS* | MINGW*) os_name="Windows" ;; Darwin) os_name=$(sw_vers -productName) # macOS os_version=$(sw_vers -productVersion) # 11.1 install_package="brew install" ;; Linux | GNU*) if [[ $(command -v lsb_release) ]]; then # 'normal' Linux distributions os_name=$(lsb_release -i) # Ubuntu os_version=$(lsb_release -r) # 20.04 else # Synology, QNAP, os_name="Linux" fi [[ -x /bin/apt-cyg ]] && install_package="apt-cyg install" # Cygwin [[ -x /bin/dpkg ]] && install_package="dpkg -i" # Synology [[ -x /opt/bin/ipkg ]] && install_package="ipkg install" # Synology [[ -x /usr/sbin/pkg ]] && install_package="pkg install" # BSD [[ -x /usr/bin/pacman ]] && install_package="pacman -S" # Arch Linux [[ -x /usr/bin/zypper ]] && install_package="zypper install" # Suse Linux [[ -x /usr/bin/emerge ]] && install_package="emerge" # Gentoo [[ -x /usr/bin/yum ]] && install_package="yum install" # RedHat RHEL/CentOS/Fedora [[ -x /usr/bin/apk ]] && install_package="apk add" # Alpine [[ -x /usr/bin/apt-get ]] && install_package="apt-get install" # Debian [[ -x /usr/bin/apt ]] && install_package="apt install" # Ubuntu ;; esac debug "$info_icon System OS : $os_name ($os_kernel) $os_version on $os_machine" debug "$info_icon Package mgt: $install_package" # get last modified date of this script script_modified="??" [[ "$os_kernel" == "Linux" ]] && script_modified=$(stat -c %y "$script_install_path" 2>/dev/null | cut -c1-16) # generic linux [[ "$os_kernel" == "Darwin" ]] && script_modified=$(stat -f "%Sm" "$script_install_path" 2>/dev/null) # for MacOS debug "$info_icon Last modif : $script_modified" debug "$info_icon Script ID : $script_lines lines / md5: $script_hash" debug "$info_icon Creation : $script_created" debug "$info_icon Running as : $USER@$HOSTNAME" # if run inside a git repo, detect for which remote repo it is if git status &>/dev/null; then readonly git_repo_remote=$(git remote -v | awk '/(fetch)/ {print $2}') debug "$info_icon git remote : $git_repo_remote" readonly git_repo_root=$(git rev-parse --show-toplevel) debug "$info_icon git folder : $git_repo_root" else readonly git_repo_root="" readonly git_repo_remote="" fi # get script version from VERSION.md file - which is automatically updated by pforret/setver [[ -f "$script_install_folder/VERSION.md" ]] && script_version=$(cat "$script_install_folder/VERSION.md") # get script version from git tag file - which is automatically updated by pforret/setver [[ -n "$git_repo_root" ]] && [[ -n "$(git tag &>/dev/null)" ]] && script_version=$(git tag --sort=version:refname | tail -1) } prep_log_and_temp_dir() { tmp_file="" log_file="" if [[ -n "${tmp_dir:-}" ]]; then folder_prep "$tmp_dir" 1 tmp_file=$(mktemp "$tmp_dir/$execution_day.XXXXXX") debug "$config_icon tmp_file: $tmp_file" # you can use this temporary file in your program # it will be deleted automatically if the program ends without problems fi if [[ -n "${log_dir:-}" ]]; then folder_prep "$log_dir" 30 log_file="$log_dir/$script_prefix.$execution_day.log" debug "$config_icon log_file: $log_file" fi } import_env_if_any() { env_files=("$script_install_folder/.env" "$script_install_folder/$script_prefix.env" "./.env" "./$script_prefix.env") for env_file in "${env_files[@]}"; do if [[ -f "$env_file" ]]; then debug "$config_icon Read config from [$env_file]" # shellcheck disable=SC1090 source "$env_file" fi done } initialise_output # output settings lookup_script_data # find installation folder [[ $run_as_root == 1 ]] && [[ $UID -ne 0 ]] && die "user is $USER, MUST be root to run [$script_basename]" [[ $run_as_root == -1 ]] && [[ $UID -eq 0 ]] && die "user is $USER, CANNOT be root to run [$script_basename]" init_options # set default values for flags & options import_env_if_any # overwrite with .env if any if [[ $sourced -eq 0 ]]; then parse_options "$@" # overwrite with specified options if any prep_log_and_temp_dir # clean up debug and temp folder main # run main program safe_exit # exit and clean up else # just disable the trap, don't execute main trap - INT TERM EXIT fi