Micheal Perr
Micheal Perr

Reputation: 1895

Find out if file has changed

I want to find out if a file has been modified since the last time my shell script was started, maybe by creating a boolean or something... Maybe it would be possible to save the last time the script was run in a text file and when the script is started the next time it should read this file and then it should find out what file have been changed so that I can check if a file has changed using something like:

for file in *
do
    #Somecode here
    if [ $filehaschanged != "0" ]; then
    echo "Foobar" > file.txt
    fi
    #Somecode here
done

Maybe it would be possible to do this with find...any ideas?

Upvotes: 15

Views: 29422

Answers (4)

firebush
firebush

Reputation: 5850

After a fair bit of research, it seems like a portable way to make use of timestamps to determine if files have been changed is challenging. ls is portable but tricky to parse. stat is not portable. In the end, @kev's answer to use a file as a point of reference combined with find seems to be the best method I've found so far. I'll contribute to the discussion by providing a few examples to demonstrate how to do this.

find -newer

This first example demonstrates how find's -newer argument works:

$ touch a
$ touch b

# Note that b is newer than a. Thus:
$ find b -newer a
b

# a is the same age as a, so it isn't printed here:
$ find a -newer a
$

Application of find -newer

Applying this behavior, we can determine what files were touched by an invocation of a script like so:

# Create the reference file:
touch /tmp/some_file

# Now run a script that may touch certain files in ./dir:
./touch_some_files_in_a_directory.sh ./dir

# Find all files in ./dir modified since /tmp/some_file was touched.
find ./dir -newer /tmp/some_file

I've been able to use find with -newer on Linux systems and MacOS. I can't speak to whether this works on other platforms.

Upvotes: 1

oao
oao

Reputation: 11

kev's solution in Python, works if you have permissions to touch the script:

#!/usr/bin/python 
import os
import sys

files= ('a.txt', 'b.txt')
me= sys.argv[0]
mytime= os.path.getmtime(me)
for f in files:
    ft= os.path.getmtime(f)
    if ft > mytime:
        print f, "changed"
os.utime(me, None)

Upvotes: 1

ghoti
ghoti

Reputation: 46836

Michael, by "changed", are you asking if the file has been touched (i.e. datestamp is newer), or are you asking if the content is different?

If the former, you can test this with find or test. For example, in shell:

#!/bin/sh
touch file1
sleep 1
touch file2
if [ "file1" -nt "file2" ]; then
  echo "This will never be seen."
else
  echo "Sure enough, file1 is older."
fi

If what you're looking for is a test of the contents, then your operating system probably includes something that will test the hash of a file.

[ghoti@pc ~]$ date > testfile
[ghoti@pc ~]$ md5 testfile
MD5 (testfile) = 1b2faf8be02641f37e6d87b15444417d
[ghoti@pc ~]$ cksum testfile
3778116869 29 testfile
[ghoti@pc ~]$ sha1 testfile 
SHA1 (testfile) = 5f4076a3828bc23a050be4867549996180c2a09a
[ghoti@pc ~]$ sha256 testfile
SHA256 (testfile) = f083afc28880319bc31417c08344d6160356d0f449f572e78b343772dcaa72aa
[ghoti@pc ~]$ 

I'm in FreeBSD. If you're in Linux, then you probably have "md5sum" instead of "md5".

To put this into a script, you'd need to walk through your list of files, store their hashes, then have a mechanism to test current files against their stored hashes. This is easy enough to script:

[ghoti@pc ~]$ find /bin -type f -exec md5 {} \; > /tmp/md5list
[ghoti@pc ~]$ head -5 /tmp/md5list
MD5 (/bin/uuidgen) = 5aa7621056ee5e7f1fe26d8abb750e7a
MD5 (/bin/pax) = 7baf4514814f79c1ff6e5195daadc1fe
MD5 (/bin/cat) = f1401b32ed46802735769ec99963a322
MD5 (/bin/echo) = 5a06125f527c7896806fc3e1f6f9f334
MD5 (/bin/rcp) = 84d96f7e196c10692d5598a06968b0a5

You can store this (instead of /bin run it against whatever's important, perhaps /) in a predictable location, then write a quick script to check a file against the hash:

#!/bin/sh

sumfile=/tmp/md5list

if [ -z "$1" -o ! -f "$1" ]; then
  echo "I need a file."
  exit 1
elif ! grep -q "($1)" $sumfile; then
  echo "ERROR: Unknown file: $1."
  exit 1
fi

newsum="`md5 $1`"

if grep -q "$newsum" $sumfile; then
  echo "$1 matches"
else
  echo "$1 IS MODIFIED"
fi

This kind of script is what tools like tripwire provide.

Upvotes: 24

kev
kev

Reputation: 161654

You can touch all files when you run your script. Then touch the script itself.
Next time, you just find any files which is newer than your script.

Upvotes: 3

Related Questions