Last active
August 22, 2017 08:13
-
-
Save cojack/4d92c8ad84b23cd197c1b48e696f8f83 to your computer and use it in GitHub Desktop.
Shell script template https://www.uxora.com/unix/shell-script/18-shell-script-template
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
##!/bin/ksh | |
#================================================================ | |
# HEADER | |
#================================================================ | |
#% SYNOPSIS | |
#+ ${SCRIPT_NAME} [-hv] [-o[file]] args ... | |
#% | |
#% DESCRIPTION | |
#% This is a script template | |
#% to start any good shell script. | |
#% | |
#% OPTIONS | |
#% -o [file], --output=[file] Set log file (default=/dev/null) | |
#% use DEFAULT keyword to autoname file | |
#% The default value is /dev/null. | |
#% -t, --timelog Add timestamp to log ("+%y/%m/%d@%H:%M:%S") | |
#% -x, --ignorelock Ignore if lock file exists | |
#% -h, --help Print this help | |
#% -v, --version Print script information | |
#% | |
#% EXAMPLES | |
#% ${SCRIPT_NAME} -o DEFAULT arg1 arg2 | |
#% | |
#================================================================ | |
#- IMPLEMENTATION | |
#- version ${SCRIPT_NAME} (www.uxora.com) 0.0.4 | |
#- author Michel VONGVILAY | |
#- copyright Copyright (c) http://www.uxora.com | |
#- license GNU General Public License | |
#- script_id 12345 | |
#- | |
#================================================================ | |
# HISTORY | |
# 2015/03/01 : mvongvilay : Script creation | |
# 2015/04/01 : mvongvilay : Add long options and improvements | |
# | |
#================================================================ | |
# DEBUG OPTION | |
# set -n # Uncomment to check your syntax, without execution. | |
# set -x # Uncomment to debug this shell script | |
# | |
#================================================================ | |
# END_OF_HEADER | |
#================================================================ | |
trap 'error "${SCRIPT_NAME}: FATAL ERROR at $(date "+%HH%M") (${SECONDS}s): Interrupt signal intercepted! Exiting now..." | |
2>&1 | tee -a ${fileLog:-/dev/null} >&2 ; | |
exit 99;' INT QUIT TERM | |
trap 'cleanup' EXIT | |
#============================ | |
# FUNCTIONS | |
#============================ | |
#== exec_cmd function ==# | |
exec_cmd() { | |
{ | |
${*} | |
rc_save | |
} 2>&1 | fecho CAT "${*}" | |
rc_restore | |
rc_assert "Command failed: ${*}" | |
return $rc ; | |
} | |
#== fecho function ==# | |
fecho() { | |
myType=${1} ; shift ; | |
[[ ${SCRIPT_TIMELOG_FLAG:-0} -ne 0 ]] && printf "$( date ${SCRIPT_TIMELOG_FORMAT} )" | |
printf "[${myType%[A-Z][A-Z]}] ${*}\n" | |
if [[ "${myType}" = CAT ]]; then | |
if [[ ${SCRIPT_TIMELOG_FLAG:-0} -eq 0 ]]; then | |
cat -un - | awk '$0="[O] "$0; fflush();' ; | |
elif [[ "${GNU_AWK_FLAG}" ]]; then # fast - compatible linux | |
cat -un - | awk -v tformat=${SCRIPT_TIMELOG_FORMAT#+} '$0=strftime(tformat)"[O] "$0; fflush();' ; | |
else # average speed and more resource intensive- compatible unix/linux | |
cat -un - | while read LINE; do \ | |
[[ ${OLDSECONDS:=$(( ${SECONDS}-1 ))} -lt ${SECONDS} ]] && OLDSECONDS=$(( ${SECONDS}+1 )) \ | |
&& TSTAMP="$( date ${SCRIPT_TIMELOG_FORMAT} )"; printf "${TSTAMP}[O] ${LINE}\n"; \ | |
done | |
fi | |
fi | |
} | |
#== custom function ==# | |
check_cre_file() { | |
myFile=${1} | |
[[ "${myFile}" = "/dev/null" ]] && return 0 | |
[[ -e ${myFile} ]] && error "${SCRIPT_NAME}: ${myFile}: File already exists" && return 1 | |
touch ${myFile} 2>&1 1>/dev/null | |
[[ $? -ne 0 ]] && error "${SCRIPT_NAME}: ${myFile}: Cannot create file" && return 2 | |
rm -f ${myFile} 2>&1 1>/dev/null | |
[[ $? -ne 0 ]] && error "${SCRIPT_NAME}: ${myFile}: Cannot delete file" && return 3 | |
return 0 | |
} | |
#============================ | |
# ALIAS AND FUNCTIONS | |
#============================ | |
#== error management function ==# | |
info() { fecho INF "${*}"; } | |
warning() { fecho WRN "WARNING: ${*}" 1>&2; countWrn=$(( ${countWrn} + 1 )); } | |
error() { fecho ERR "ERROR: ${*}" 1>&2; countErr=$(( ${countErr} + 1 )); } | |
cleanup() { [[ -e "${fileRC}" ]] && rm ${fileRC}; [[ -e "${fileLock}" ]] && [[ "$( head -1 ${fileLock} )" = "${EXEC_ID}" ]] && rm ${fileLock}; } | |
scriptfinish() { [[ $rc -eq 0 ]] && endType="INF" || endType="ERR"; | |
fecho ${endType} "${SCRIPT_NAME} finished at $(date "+%HH%M") (Time=${SECONDS}s, Error=${countErr}, Warning=${countWrn}, RC=$rc)."; | |
exit $rc ; } | |
#== usage function ==# | |
usage() { printf "Usage: "; head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#+" | sed -e "s/^#+[ ]*//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; } | |
usagefull() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#[%+-]" | sed -e "s/^#[%+-]//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; } | |
scriptinfo() { head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "^#-" | sed -e "s/^#-//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"; } | |
#== inter program return code function ==# | |
rc_save() { rc=$? && echo $rc > ${fileRC} ; } | |
rc_restore() { [[ -r "${fileRC}" ]] && rc=$(cat ${fileRC}) ; } | |
rc_assert() { [[ $rc -ne 0 ]] && error "${*} (RC=$rc)"; } | |
#============================ | |
# FILES AND VARIABLES | |
#============================ | |
#== general variables ==# | |
SCRIPT_HEADSIZE=$(head -200 ${0} |grep -n "^# END_OF_HEADER" | cut -f1 -d:) | |
SCRIPT_ID="$(scriptinfo | grep script_id | tr -s ' ' | cut -d' ' -f3)" | |
SCRIPT_NAME="$(basename ${0})" # scriptname without path | |
SCRIPT_UNIQ="${SCRIPT_NAME%.*}.${SCRIPT_ID}.$(date "+%y%m%d%H%M%S").${$}" | |
SCRIPT_DIR="$( cd $(dirname "$0") && pwd )" # script directory | |
SCRIPT_DIR_TEMP="/tmp" # Make sure temporary folder is RW | |
SCRIPT_TIMELOG_FLAG=0 | |
SCRIPT_TIMELOG_FORMAT="+%y/%m/%d@%H:%M:%S" | |
HOSTNAME="$(hostname)" | |
FULL_COMMAND="${0} $*" | |
EXEC_DATE=$(date "+%y%m%d%H%M%S") | |
EXEC_ID=${$} | |
GNU_AWK_FLAG="$(awk --version 2>/dev/null | head -1 | grep GNU)" | |
fileRC="${SCRIPT_DIR_TEMP}/${SCRIPT_UNIQ}.tmp.rc"; | |
fileLock="${SCRIPT_DIR_TEMP}/${SCRIPT_NAME%.*}.${SCRIPT_ID}.lock" | |
fileLog="/dev/null" | |
rc=0; | |
countErr=0; | |
countWrn=0; | |
#== option variables ==# | |
flagOptErr=0 | |
flagOptLog=0 | |
flagOptTimeLog=0 | |
flagOptIgnoreLock=0 | |
#============================ | |
# PARSE OPTIONS WITH GETOPTS | |
#============================ | |
#== set short options ==# | |
SCRIPT_OPTS=':o:txhv-:' | |
#== set long options associated with short one ==# | |
typeset -A ARRAY_OPTS | |
ARRAY_OPTS=( | |
[timelog]=t | |
[ignorelock]=x | |
[output]=o | |
[help]=h | |
[man]=h | |
) | |
#== parse options ==# | |
while getopts ${SCRIPT_OPTS} OPTION ; do | |
#== translate long options to short ==# | |
if [[ "x$OPTION" == "x-" ]]; then | |
LONG_OPTION=$OPTARG | |
LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2) | |
LONG_OPTIND=-1 | |
[[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1) | |
[[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND" | |
OPTION=${ARRAY_OPTS[$LONG_OPTION]} | |
[[ "x$OPTION" = "x" ]] && OPTION="?" OPTARG="-$LONG_OPTION" | |
if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then | |
if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then | |
OPTION=":" OPTARG="-$LONG_OPTION" | |
else | |
OPTARG="$LONG_OPTARG"; | |
if [[ $LONG_OPTIND -ne -1 ]]; then | |
[[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 )) | |
shift $OPTIND | |
OPTIND=1 | |
fi | |
fi | |
fi | |
fi | |
#== options follow by another option instead of argument ==# | |
if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then | |
OPTARG="$OPTION" OPTION=":" | |
fi | |
#== manage options ==# | |
case "$OPTION" in | |
o ) fileLog="${OPTARG}" | |
[[ "${OPTARG}" = *"DEFAULT" ]] && fileLog="$( echo ${OPTARG} | sed -e "s/DEFAULT/${SCRIPT_UNIQ}.log/g" )" | |
flagOptLog=1 | |
;; | |
t ) flagOptTimeLog=1 | |
SCRIPT_TIMELOG_FLAG=1 | |
;; | |
x ) flagOptIgnoreLock=1 | |
;; | |
h ) usagefull | |
exit 0 | |
;; | |
v ) scriptinfo | |
exit 0 | |
;; | |
: ) error "${SCRIPT_NAME}: -$OPTARG: option requires an argument" | |
flagOptErr=1 | |
;; | |
? ) error "${SCRIPT_NAME}: -$OPTARG: unknown option" | |
flagOptErr=1 | |
;; | |
esac | |
done | |
shift $((${OPTIND} - 1)) ## shift options | |
#============================ | |
# MAIN SCRIPT | |
#============================ | |
[ $flagOptErr -eq 1 ] && usage 1>&2 && exit 1 ## print usage if option error and exit | |
#== Check/Set arguments ==# | |
[[ $# -gt 2 ]] && error "${SCRIPT_NAME}: Too many arguments" && usage 1>&2 && exit 2 | |
#== Check files ==# | |
check_cre_file ${fileRC} || exit 3 | |
check_cre_file ${fileLog} || exit 3 | |
if [[ ${flagOptIgnoreLock} -eq 0 ]]; then | |
[[ -e ${fileLock} ]] && error "${SCRIPT_NAME}: ${fileLock}: lock file detected" && exit 4 | |
check_cre_file ${fileLock} || exit 4 | |
fi | |
#== Create files ==# | |
[[ "${fileLog}" != "/dev/null" ]] && touch ${fileLog} && fileLog="$( cd $(dirname "${fileLog}") && pwd )"/"$(basename ${fileLog})" | |
[[ ! -e ${fileLock} ]] && echo "${EXEC_ID}" > ${fileLock} | |
#== Main part ==# | |
#===============# | |
{ trap 'kill -TERM ${$}; exit 99;' TERM | |
info "${SCRIPT_NAME}: start $(date "+%y/%m/%d@%H:%M:%S") with process id ${EXEC_ID} by ${USER}@${HOSTNAME}:${PWD}"\ | |
$( [[ ${flagOptLog} -eq 1 ]] && echo " (LOG: ${fileLog})" || echo " (NOLOG)" ); | |
#== start your program here ==# | |
exec_cmd "ls -lrt $*" | |
#sleep 5 | |
#== end your program here ==# | |
scriptfinish ; } 2>&1 | tee ${fileLog} | |
#== End ==# | |
#=========# | |
rc_restore | |
exit $rc |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment