diff --git a/onfilechange.sh b/onfilechange.sh new file mode 100644 index 0000000000000000000000000000000000000000..56cddeff7eb1293f382f912cf3298ec1f76d5c7d --- /dev/null +++ b/onfilechange.sh @@ -0,0 +1,290 @@ +#!/bin/bash +# ====================================================================== +# +# +# T R I G G E R C O M M A N D O N A F I L E C H A N G E +# +# +# A Shell script that watches a given file or multiple files. +# If the file changes then a given command will be exxecuted. +# It loops permanently; you need to stop it by Ctrl + C and/ or can +# use it as systemd watcher daemon. +# +# It uses stats for wide compatibility but prefers inotifywatch to +# check a file change. +# +# licence: GNU GPL 3.0 +# +# ---------------------------------------------------------------------- +# 2019-10-14 v1.0 <axel.hahn@iml.unibe.ch> first basic version +# ====================================================================== + +# ---------------------------------------------------------------------- +# CONFIG +# ---------------------------------------------------------------------- + +bDebug=0 +iSleep=5 +sCommand= +sWatchFile= +sMode= + +# ---- below are some internal variables +sVersion=1.0 + + +# ---------------------------------------------------------------------- +# FUNCTIONS +# ---------------------------------------------------------------------- + +# show help +function showHelp(){ +cat <<ENDOFHELP +HELP: + This script checks the change of a given file and triggers + a command if it changes + +PRAMETERS: + -c [command] + command to execute on a file change + -f [filename(s)] + filenames to watch; separate multiple files with space and put all in quotes + -h + show this help + -i + force inotifywait command + -s + force stats command + -v + verbose mode; enable showing debug output + -w [integer] + for stats mode: wait time in seconds betweeen each test or on missing file; default: 5 sec + +EXAMPLES: + `basename $0` -f /home/me/touchfile.txt -c "ls -l" + watch touchfile.txt and make a file listing on change + + `basename $0` -f "/home/me/touchfile.txt home/me/touchfile2.txt" -c "ls -l" + watch touchfile.txt and touchfile2.txt + + `basename $0` -f /home/me/touchfile.txt -s -w 10 -c "ls -l" + watch touchfile.txt every 10 sec with stats and make a file listing on change + +ENDOFHELP + +} + +# write debug output ... if debug is enabled only +# +# global (bool) $bDebug +# param string text message to show +function wd(){ + if [ $bDebug -ne 0 ]; then + echo "[`date`] DEBUG " $* + fi +} + +# list watched files +# +# global (string) filename(s) to watch +function listFiles(){ + echo + echo ">>>>> watched files" + ls -ld ${sWatchFile} 2>&1 +} + +# for stats: helper to get current file status +# +# global (string) $sWatchFile +function getFilestatus(){ + for myfile in ${sWatchFile} + do + stat -c "%F %n | perms: %A; user %u (%U) group %g (%G) | size: %s byte | last modification %y" "${myfile}" 2>&1 + done +} + +# for stats: inititalize file change detection +# +# global (string) $TmpFile last/ initial file status +# global (string) $sTmpFile2 current file status +function initFilestatus(){ + getFilestatus >${sTmpFile} + wd "`cat ${sTmpFile}`" + cp -p ${sTmpFile} ${sTmpFile2} +} + +# for stats: compare file status and execute command on change +# +# global (string) $TmpFile last/ initial file status +# global (string) $sTmpFile2 current file status +# global (string) $sCommand command to execute +function compareFilestatus(){ + getFilestatus >${sTmpFile2} + wd "`cat ${sTmpFile2}`" + diff ${sTmpFile} ${sTmpFile2} + if [ $? -eq 0 ]; then + wd "No Change" + else + wd "Change detected." + execCommand + if [ $? -eq 0 ]; then + echo Command was successfuil. + echo + wd "Re-Init File status" + initFilestatus + else + echo FAILED. + fi + fi +} + +# execute a command; called on a file change +# +# global (string) $sCommand command line to exectute +function execCommand(){ + listFiles + echo + echo ">>>>> `date` Executing ${sCommand} ..." + ${sCommand} +} + +# ---------------------------------------------------------------------- +# MAIN +# ---------------------------------------------------------------------- + +cat <<ENDOFHEAD +____________________________________________________________________________________ + + T R I G G E R C O M M A N D O N A F I L E C H A N G E +____________________________________________________________________________________ + v${sVersion} + +ENDOFHEAD + +which inotifywait >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo ERROR: the command inotify was not found on your system, + which stat >/dev/null 2>&1 + if [ $? -ne 0 ]; then + echo ERROR: the command stat was not found on your system, + exit 2 + fi + echo "INFO: enabling compatibility mode with stats command" + sMode=stats +else + echo "INFO: inotifywait command detected" + sMode=inotifywait +fi + +if [ $# -eq 0 ]; then + showHelp + exit 0 +fi + +while getopts ":c: :v :f: :h :i :s :w:" opt +do + case $opt in + \?|h) + showHelp + exit 0 + ;; + v) + bDebug=1 + wd "debug is now ${bDebug}" + ;; + c) + sCommand=$OPTARG + wd "command is now ${sCommand}" + ;; + f) + sWatchFile=$OPTARG + wd "watch file is now ${sWatchFile}" + ;; + i) + sMode=inotifywait + wd "forcing mode with inotifywait command" + ;; + s) + sMode=stats + wd "forcing mode with stats command" + ;; + w) + typeset -i iSleep=$OPTARG + wd "sleep $iSleep sec" + ;; + :) + echo "ERROR: Option -$opt requires an argument." >&2 + showHelp + exit 1 + esac +done + +cat <<ENDOFINFO +checking file [${sWatchFile}] +with command [${sMode}] +with a sleep time of [${iSleep}] seconds +and on change start command [${sCommand}] + +............................................................................... + + +ENDOFINFO + +# ---------------------------------------------------------------------- +# CHECKS +# ---------------------------------------------------------------------- + +wd "--- checks" +if [ -z "${sWatchFile}" ]; then + echo ERROR: set a check file with param -f + exit 1 +fi +listFiles +if [ $? -ne 0 ]; then + echo "INFO: file ${sWatchFile} (or one of them) does not exist yet" + # echo "ERROR: file ${sWatchFile} (or one of them) does not exist yet" + # exit 1 +fi + +if [ -z "${sCommand}" ]; then + echo ERROR: set a ${sCommand} with param -s + exit 1 +fi + +# ---------------------------------------------------------------------- +# GO +# ---------------------------------------------------------------------- + +myset=`echo ${sWatchFile} | sha1sum | cut -f 1 -d " "` +sTmpFile="/tmp/`basename $0`-${myset}-last.tmp" +sTmpFile2="/tmp/`basename $0`-${myset}-current.tmp" + +case $sMode in + "inotifywait") + while true; do + listFiles >/dev/null 2>&1 + if [ $? -eq 0 ]; then + inotifywait -e attrib -e modify ${sWatchFile} && execCommand + else + wd sleep ${iSleep} + sleep ${iSleep} + echo "WARNING: inotifywait only can notify if all watched files exist. You can use -s to use stats for file detection" + fi + done + ;; + + "stats") + wd "--- init" + initFilestatus + echo waiting for file changes ... + + wd "--- starting loop" + while true; do + wd sleep ${iSleep} + sleep ${iSleep} + compareFilestatus + done + ;; +esac + +# ----------------------------------------------------------------------