0

First of all, I am pretty inexperienced in this, but I need a particular bash script for one of my gaming servers. The bash script should simply determine whether a particual text document receives changes periodically in its content or not.

If there do exist any kind of changes within 30 seconds, the script should do:

echo "text file content is changing"

If there do not exist any kind of changes within 30 seconds, the script should do:

echo "text file content is not changing"

After the bash script determined the content changes, it should repeat itself continuously.

EDIT: Thanks for all your help guys! I have another concern, is it possible that the script is determining automatically the latest created/edited text file in a particular folder? That means if even a new text file was created in that folder while the script is determining the previous text file, it should cancel the process and determine the new (latest) file.

Thanks in advance! :-)

2 Answers2

2

I use /usr/bin/stat --format='%Y' on my config file, and make a private copy whenever "%Y time of last data modification, seconds since Epoch" changes. This way, I don't need to read the whole file just to check the modification time.

Unrunnable code fragments:

original=""                     # set the <config.file>
original_update=0               # time <config.file> was last modified
config="/var/tmp/${me}.$$.config" # my writable copy of config

...

function up-to-date () {
    # updates configuration file if necessary
    new_update="$(/usr/bin/stat --format='%Y' $original )"
    if [[  "$new_update" -ne "$original_update" ]] ; then
        if [[ $(countconfiglines "$original") -eq 0  ]] ; then
            echo "Invalid configuration in $original" >&2
            exit 4
        else
            /bin/cp --force "$original" "$config"
            original_update="$new_update"
        fi
    fi
    }

... 


else
    # watch for changes, record "%Y time of last modification,
    # seconds since Epoch"
    original_update="$(/usr/bin/stat --format='%Y' $original )"

    # make a writeable copy for our use, and clean it up at the end
    # unless $debug
    [[ $debug -ne 0 ]] || trap "/bin/rm -f $config" EXIT
    /bin/cp --force "$original" "$config"

fi

And I call up-to-date every time around my main loop, when I'm willing to parse a new config file.

waltinator
  • 36,399
  • What do you mean with "Unrunnable code fragments"? I am very inexperienced with this scripting and I dont know how to edit your script. Thanks anyway for helping me :-) – Phillip Apr 13 '17 at 18:14
  • I like this answer (hence the up-vote), but it will only catch legit changes to the file. Corruption or changes by malware might not be detected. Then again, there is no indication that the question's author is interested in that at all. – b_laoshi Apr 14 '17 at 01:14
  • This approach while faster than using checksums will also return false positives if a file is simply touched, while remaining unmodified. – b_laoshi Apr 14 '17 at 02:52
0

Watching individual files

Use file checksums here's a sample script that would get you going.

#!/bin/bash

# do not put tabs before paths for second file through last file
files_to_check="/path/to/first/file
/path/to/second/file
/path/to/.../file"

delay=30

while true
do
    while read path
    do
        [ ! -e "$path.sha256" ] && sha256sum "$path" > "$path.sha256"
        status=$(sha256sum --quiet -c "$path.sha256"  > /dev/null 2>&1 && echo "No change!" || (echo "File changed!" && sha256sum "$path" > "$path.sha256"))
        [ "$status" == "No change!" ] && color="\e[0m" || color="\e[38;5;208m"
        printf "$color[%s] %s: %s\e[0m\n" $(date +%Y%m%d.%H%M%S) "$path" "$status"
    done <<< "$files_to_check"
    sleep $delay
    echo "-----------"
done

If you just want to check an entire directory recursively, you could pass the output of find /dir/to/check -type f to the while loop instead of the file list.

Output from the script above looks something like this

[20170413.095401] a: No change!
[20170413.095401] b: No change!
-----------
[20170413.095431] a: No change!
[20170413.095431] b: No change!
-----------
[20170413.095501] a: File changed!
[20170413.095501] b: No change!

Scanning an entire directory and watching for addition/deletion/modification of files

This script will scan an entire directory every 30 seconds (not including the time it takes to run the scan) and color additions (green), removals (red), and changes (orange).

#!/bin/bash

# do not put tabs before paths for second file through last file
dir_to_check="/path/to/directory"
delay=30

# please note that the time it takes to generate checksums will produce more time
# between loops than the actual delay
while true
do
    while read path
    do
        [ ! -e "${path/.sha256/}" ] && printf "\e[38;5;196m%s\e[0m\n" "[$(date +%Y%m%d.%H%M%S)] File removed: ${path/.sha256/}" && rm "$path"
    done <<< "$(find $dir_to_check -type f -iname "*\.sha256" | sort)"

    while read path
    do
        if [ ! -e "$path.sha256" ]; then
            printf "\e[38;5;046m%s\e[0m\n" "[$(date +%Y%m%d.%H%M%S)] New file found: $path (modified $(date --date="@$(stat --format="%Y" "$path")" +%Y%m%d.%H%M%S))"
            sha256sum "$path" > "$path.sha256"
        else
            status=$(sha256sum --quiet -c "$path.sha256"  > /dev/null 2>&1 && echo "No change!" || (echo "File changed! ($(date --date="@$(stat --format="%Y" "$path")" +%Y%m%d.%H%M%S))" && sha256sum "$path" > "$path.sha256"))
            [ "$status" == "No change!" ] && color="\e[0m" || color="\e[38;5;208m"
            printf "$color[%s] %s: %s\e[0m\n" $(date +%Y%m%d.%H%M%S) "$path" "$status"
        fi
    done <<< "$(find $dir_to_check -type f ! -iname "*\.sha256" | sort)"
    sleep $delay
    echo "-----------"
done

Output resembles (sorry, color doesn't transfer here)

[20170414.103126] /tmp/change/a: No change!
[20170414.103126] /tmp/change/b: No change!
[20170414.103126] /tmp/change/c: No change!
[20170414.103126] /tmp/change/run.sh: File changed! (20170414.103125)
-----------

    ...

-----------
[20170414.103156] File removed: /tmp/change/a
[20170414.103156] /tmp/change/b: No change!
[20170414.103156] /tmp/change/c: No change!
[20170414.103156] /tmp/change/run.sh: No change!
-----------
[20170414.103206] New file found: /tmp/change/a (modified 20170414.103200)
[20170414.103206] /tmp/change/b: No change!
[20170414.103206] /tmp/change/c: No change!
[20170414.103206] /tmp/change/run.sh: No change!
b_laoshi
  • 4,660
  • 4
  • 25
  • 46
  • An md5sum would probably be sufficient, but creating collisions is possible, though maybe not within 30 seconds at the time of writing. If you're dealing with large files, it takes less time to calculate MD5 checksums. – b_laoshi Apr 13 '17 at 01:59
  • Thanks for this script! I will try it out in some hours :-) – Phillip Apr 13 '17 at 15:41
  • Hello again :-) your script works fine! Thank you – Phillip Apr 13 '17 at 19:16