Translate

2021-12-07

Watch changes in a directory

Updated 20230123

There are at least two cases when watching for file changes, the private computer and the server.

The difference is the frequency of the changes. In a server you might have to decide what files you want to watch to not overload the server unnecessarily with thousands of changes every second. On your own machine changes may be a single number/second per directory. This latter case is what the following script can handle.

#!/bin/bash
#
#########
#   Name: 
#       pfdirwatch.sh
#
#   Version
#       20220402
#           Changes to make the display in the terminal window more readable when starting
#           w/o "n" or "y". The terminal will roll on, never be cleared, the printout limited
#           only by the number of lines available in the terminal window
#       20220207
#           added a log in /tmp. It will be deleted each time the machine reboots.
#           Each line in the log has a time assigned. 
#           NOTE: using parentheses in comments sometimes causes error in bash scripts!! It shouldn't but it does.
#           Removed parentheses!
#       20220130
#           bugfix. (The script only checked for changes in it's own working dir)
#       20220107
#           bugfix avoiding unnecessary message
#       20220101
#           Original version
#
#   Description:
#       This script takes two snapshots of the file system and then compares
#       them with respect to file time (changes) and existence (deleted or created).
#       Since it's rather primitive, changes can happen and vanish between the points
#       in time when the snapshots are taken. I use it to check for repetitive tasks
#       that changes log files. If changes occur, the task is running.
#       
#       The time between snapshots are lengthened by the $TIME_TO_READ bcs you need
#       to get a glimpse of what happened. 
#       
#   Usage:
#       I normally use it like:
#       pfdirwatch.sh  /home/xhosa/tools/scripts 10 n
#       NOTE:It will only work if you are in the same dir as the script when starting it!!
#           this will start the script looking like
#+++++++++++++++++++++
#We are in dir /home/xhosa/tools/scripts
#Command: /home/xhosa/tools/scripts/pfdirwatch.sh /home/xhosa/tools/scripts 5 n
#Pid: 598858
#+++++++++++++++++++++ 
#
#15:46:45
#
#       Setting the "n" to a "y" will run it once with only the time displayed and the next
#       loop the "y" will have been set to "n" and it will continue like above
#
#       Leaving out the "y"/"n" as the third argument makes the program run without the 
#        header. The terminal display will roll on, the TIME_TO_READ will not limit the time
#        you can view changes (they will be readable only limited by the number of lines available
#        in the terminal).
#
#########
# --TODO--: another variant of this script, logging to a file and starting tail on it. This way I'd have both live data
# available plus a log!!
#
# change this to what suits you. If you choose /tmp these files will be cleared when restarting
# otherwise you'll have to do that yourself.
TMP="/tmp/pfdirwatch"
FILE="/tmp/pfdirwatch.sh.log"
TIME_TO_READ=6

## creating log
if [ ! -f "$FILE" ]; then
    date +%Y-%m-%d_%H:%M:%S >> $FILE
    echo -e "Command:" $0 $1 $2 $3 >> $FILE
    echo "Logfile: " $FILE
fi

   echo -n "."  >> $FILE

   ## if pfdirwatch is a file, remove it, created the dir pfdirwatch
if [[ ! -d $TMP ]]
then                        # it's a file
    if [[ -f $TMP ]]
    then
        rm $TMP > /dev/null  2&>1
    fi
    mkdir $TMP
    chmod 0750 $TMP
else
    ## pfdirwatch IS a dir, remove all files in it
    for N in `ls -i -A /tmp/pfdirwatch/`; do ((loop++));done
    if(( $loop>0 ))
    then
        rm /tmp/pfdirwatch/* > /dev/null 2&>1
    fi
    
fi

## report on terminal will be readable for this no of seconds.
TIME_TO_READ=4

# asking for the number of arguments to the script, the script itself NOT counted!
if [ $# -lt 2 ]
then echo -e \\n"Usage '$0 folder waitseconds "\\n
exit 1
fi

## checking that argument 1 is a folder
if [ ! -d $1 ]
then
echo "Folder name is false!!"
exit 1
fi

## the "n" as argument 3 will make this report printout on the screen.
if [[ $3 == 'n' ]]
then
clear
echo -e \\n+++++++++++++++++++++
echo "This script's working directory: "`pwd`
echo "Command:" $0 $1 $2 $3
pid=$BASHPID
echo "Pid: "$pid
echo -e +++++++++++++++++++++ \\n
fi
echo `date +%H:%M:%S`
arg3="n"

# find only in this dir (no deeper) only regular files, stat lists %y(modification time) %s(ize) %n(ame) 
##find  $1 -maxdepth 1 -type f -not \( -name "$pid.lista.txt"  -o -name "$pid.listab.txt" \) -exec stat -c '%y %s %n ' {}  \;
find  $1 -maxdepth 1 -type f -not \( -name "$pid.lista.txt"  -o -name "$pid.listab.txt" \) -exec stat -c '%y %s %n ' {}  \; > $TMP/$pid.lista.txt

# list looks like:
# < 2011-08-10 12:47:43.000000000 +0200 196 ./tjosan 
# > 2011-08-10 12:49:42.000000000 +0200 203 ./tjosan 
# > 2011-08-10 12:49:42.000000000 +0200 0 ./124942 

# $2 (below) is the measure of time between now and then #the snapshots#, where eventual differences among the files will be
# discovered. This value is a gas pedal, sort of. Comment it or set it to 0 and you'll have live seconds working 
# in the printout.
sleep $2

##find  $1 -maxdepth 1 -type f -not \( -name "$pid.lista.txt" -o -name "$pid.listab.txt" \) -exec stat -c '%y %s %n ' {}  \;
find  $1 -maxdepth 1 -type f -not \( -name "$pid.lista.txt" -o -name "$pid.listab.txt" \) -exec stat -c '%y %s %n ' {}  \; > $TMP/$pid.listab.txt


# "sed -n '/^[><]/p'>" 
# From sed manual: 'By default sed prints all processed input (except input that has been modified/deleted
# by commands such as d). Use -n to suppress output, and the p command to print specific lines.' 
# These are decided by "'/^"=from the beginning of the line, "[> or <]"=lines beginning with either, "p"= pls 
# print these lines.
# array for diff-data:
diff $TMP/$pid.lista.txt $TMP/$pid.listab.txt|sed -n '/^[><]/p'
diff $TMP/$pid.lista.txt $TMP/$pid.listab.txt|sed -n '/^[><]/p'> $TMP/$pid.diffdata


# checking the block size of the $TMP/$pid.diffdata file
l=`ls -s  $TMP/$pid.diffdata|awk '{print $1}'`

# the ls command sys "no blocks are reserved for this file"
#if (( l == 0 ))
#then
    #this script| temp dir| time between snapshots
# exec  $0 $1 $2 $3
#fi
cat $TMP/$pid.diffdata|awk '{print $6 }'|uniq -d > $TMP/$pid.dupdata
cat $TMP/$pid.diffdata|awk '{print $6 }'|uniq -u $TMP/$pid.dupdata $TMP/$pid.uniqdata|sort 

# create indexed array. "-A" means "associative". Here are the snapshots compared.
declare -a fs
declare -a ft
for udata in `cat $TMP/$pid.dupdata|awk '{print '$6'}'`
do
loop=0
while read ddata 
do
#echo "udata: "$udata
#echo "ddata: "`echo $ddata|awk '{print $6}'`

if [ "$udata" == "`echo $ddata|awk '{print $6}'`" ]
then
fs[$loop]=`echo $ddata|awk '{print $5}'`
# this is the file time:
ft[$loop]=`echo $ddata|awk '{print $3}'`
((loop++))
fi
done < $TMP/$pid.diffdata

if ! [ ${fs[0]} == ${fs[1]} ]
then
echo "Size of $udata has changed: ${fs[0]}  => ${fs[1]}";
arg3="y"
echo -e "Size of $udata has changed: ${fs[0]}  => ${fs[1]}"; >> $FILE
fi
if ! [ ${ft[0]} == ${ft[1]} ]
then
echo "Filetime of $udata has changed: "  `echo ${ft[0]}|cut -c -8`  " => "  `echo ${ft[1]}|cut -c -8` 
arg3="y"
        echo -e "Filetime of $udata has changed: "  `echo ${ft[0]}|cut -c -8`  " => "  `echo ${ft[1]}|cut -c -8` >>  $FILE

fi


done
for uqdata in `cat $TMP/$pid.uniqdata|awk '{print '$6'}'`
do
loop=0
while read ddata 
do
#echo "uqdata: "$uqdata
#echo "ddata: "`echo $ddata|awk '{print $6}'`
        
if [ "$uqdata" == "`echo $ddata|awk '{print $6}'`" ]
then
            DT=`date +%H:%M:%S`

if [ `echo $ddata|awk '{print $1}'` == "<" ]
then
echo ${DT}" Deleted file: "`echo $ddata|awk '{print $6}'` 
arg3="y"

                echo ${DT}" Deleted file: "`echo $ddata|awk '{print $6}'` >> $FILE

else
echo ${DT}" Added  file: "`echo $ddata|awk '{print $6}'`
arg3="y"

echo ${DT}" Added file: "`echo $ddata|awk '{print $6}'` >> $FILE
fi
fi
done < $TMP/$pid.diffdata
done
echo
# if changes are detected you'll have $TIME_TO_READ seconds to read about them
# the ls command sys "no blocks are reserved for this file"
if (( l == 0 ))
then
    #this script| temp dir| time between snapshots|"n"
exec  $0 $1 $2 $3
fi

    sleep $TIME_TO_READ

clear
# and here we go again, looping
exec  $0 $1 $2 $3

# - Example code #
# declare g as array and filling it directly from a command. Since there are
# five space-delimited fields in each stat-line and (in the current dir) 150 files
# we'll get 5 * 150 = 750 entries
# echo "1"
# declare -a g=(`find  . -maxdepth 1 -type f -not \(-name "$BASHPID.lista.txt"\) -exec stat -c '%y %s %n ' {}  \; |sort`)
# - end#

## - Example code #
## - Filling array from a file
# declare -a f
# cnt=0
# while read line
# do
#   f[$cnt]=$line
#   echo  ${f[$cnt]}
#   ((cnt++))
# done < lista.txt
## note: this means "${}" = execute, "#" = number of, f[] = the 'f' array, "@" all members of
## so this means: "what is the number of all items in the f-array?"
# echo "Number of f-items: "${#f[@]}
## end #


This is how it looks when running










and in fact, I've used it to keep an eye on the "screen_setup.sh_last_run.log" from the blogpost
https://a-zproj.blogspot.com/2021/12/xubuntux11-cant-remember-dual-monitor.html
It seems like that problem has been worked around nicely. That's me, enough irritation always causes me to use quick and dirty things instead of fixing the problem at the root.

Inga kommentarer:

Skicka en kommentar