1

I'm writing a script that monitors a folder for any file creation, deletion and changing. When one of these happens, the user is notified (every 30 seconds).

Currently, I'm using for loops to go through the files in the directory and compare them to an array of files created before hand, but I cannot differentiate between new files being added and the ones that have been modified.

The code I am using is the following.

start()
{
    files=(~/Junk/*)
    while true; do 
        loopstart=$(date +%s)
        watcher
        sleep 15
    done
}

watcher() 
{
    ls -1 ~/Junk
    echo -e "\n"
    for f in ~/Junk/*; do
        if [[ ! "${files[@]}" =~ "$f" ]]; then
            echo -e "\n$f has been added"
        else
            last_modified=$(stat -c %Z "$f")
            if [ $(($loopstart - $last_modified)) -le 15 ]; then
                echo -e "\n$f has been changed"
            fi
        fi
    done

    for f in "${files[@]}"; do
        if [ ! -e "$f" ]; then  
            echo -e "\n$f has been deleted"
        fi
    done

    files=(~/Junk/*)
}

start_watcher

I'm aware of inotify and other systems that will provide a solution; I just need a Bash-only solution.

  • You could use stat -c %W which is the creation time/ – Ralph Rönnquist Oct 29 '17 at 5:37
  • Your test for whether a file is new might not work right.   Try creating a file called credit, then start the watcher, then create a file called red and see what happens. – G-Man Oct 29 '17 at 5:47
  • 2
    Some alternative ideas... Instead of relying on just filenames consider incorporating inode numbers which are unique for each file. These numbers will stay with a file even if it is renamed. Retrieve inode number with stat -c %i filename. To determine if a file's content has changed you might use checksums instead of modification times. One way to get a checksum is cksum filename. – B Layer Oct 29 '17 at 8:50
  • What about symlinks, hardlinks, and file names beginning with a dot, can all that be ignored? – Hauke Laging Oct 29 '17 at 9:59
  • You've asked for a "bash-only solution". Do you really intend to discount grep, find, and other external tools? If not, please could you clarify why inotify-based answers are no use to you. Thanks – roaima Oct 29 '17 at 10:59
1

You can use the output of

find . -mindepth 1 -maxdepth 1 -type f -printf '%C@ %T@ ' -ls

which is a lot faster than calling stat (especially several times) per file. The combination of -printf and -ls has a better handling of strange file names. If you can be sure such ones will not occur then you can use

-printf '%i %C@ %T@ %P'

instead.

The inode (as mentioned in the comments) identifies the object. The ctime and mtime tell you whether the file data or metadata has been written (does not tell you whether it has really changed, though). You should write these data to arrays in a

find ... | while IFS= read ctime mtime inode dummy1 ... dummy9 rest; do ...

loop.

0

The issue is the very end of the script. I'll repost your script with the correction.

change "start_watcher" to "start" since you are trying to run the start function.

0

Try to use Auditd

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/chap-system_auditing

For monitoring a directory, you need

auditctl -w /path/to/dir -p wa -k CHANGED_ON_DIR

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.