How to log access to a specific folder and changes in its contents?

I need to track folder access time, and want to know what changes are made to it.

How do I trigger those events? Is there a way to run a specific .sh file when the folder is opened?


I assume you need to know the (clock-) time the folder is opened in e.g. nautilus, not the time it takes to access the folder.

Using the window list

You can get the window list from the command wmctrl -l, and see if the folder's name occurs in the list. The loop to check would however take at least a split second to notice the folder is opened.

You'd have wmctrl to be installed:

sudo apt-get install wmctrl

In the example below, the script runs a command when a folder is accessed for the first time, and exits.

How to use:

  • Paste the script into an empty file
  • Save it as access_time.py
  • Change in the head section of the script "<command_to_run>" by your command (between quotes)
  • Run it either with the command:

    python3 </path/to/script> <foldername_without_path>
    

    or, if you made it executable:

    </path/to/access_time.py> <foldername_without_path>
    
#!/usr/bin/env python3
import subprocess
import sys
#--- replace "<command_to_run>" with your command (between quotes):
command = "<command_to_run>"
#---
foldername = sys.argv[1]
while True:
    try:
        test = subprocess.check_output(["wmctrl", "-l"]).decode("utf-8")
    except subprocess.CalledProcessError:
        pass
    if foldername in test:
        subprocess.call(["/bin/bash", "-c", command])
        break

Edit

You can however make it work "all-in-one", so you do not need another script. The script below creates a file in your $HOME directory with the time your folder was accessed.:

#!/usr/bin/env python3
import subprocess
import sys
import os
import time
home = os.environ["HOME"]
foldername = sys.argv[1]

#--- the path your file is saved to (change if you want to, but use full paths)
path = home
#---

while True:
    try:
        test = subprocess.check_output(["wmctrl", "-l"]).decode("utf-8")
    except subprocess.CalledProcessError:
        pass
    if foldername in test:
        with open(path+"/time_data.txt", "a") as out:
            out.write("the folder "+foldername+" was opened "+time.ctime()+"\n")
        break
  • Use it just like the first option (but obviously you don't need to set the command)
  • Place a dot before the filename to make it a hidden file (press Ctrl+H to toggle visability):

    If you want that, change:

    with open(path+"/time_data.txt", "a") as out:
    

    into:

    with open(path+"/.time_data.txt", "a") as out:
    

    (Mind the indent!)

Edit 2

From your comments, and the discussion in chat, I understand that you are actually looking for a tool to log access to a folder (e.g. by nautilus) and changes in the contents of it.
As an extra option, a comprehensive log script that records in two different threads:

  • All occasions the folder was accessed by e.g. nautilus, logged in a file access_log.txt
  • All occasions that the folder's window was closed, also logged in access_log.txt
  • All files that where either added to (recursively), or removed from the directory, logged into a file directory_log.txt

These events are logged in two different files, because the logs have different refresh times. Real-time "recording" of what happens to a large directory with a lot of subdirectories is not something you would want to be done every 5 seconds or so. The consequence is that:

  • the access log has (as I set it) an accuracy of 0.5 seconds
  • the directory log (adding/removing files) has an accuracy 10 minutes. Events will be reported within 10 minutes after taking place, with a time stamp accuracy of 10 minutes.

    I tested it on a (network-) directory of ~800 GB. If your directory is much smaller, the directory log- cycle can be (much) smaller as well. I tested it for example on a 20 GB directory, with a (directory log) cycle of 10 seconds.

Example output access_log.txt:

---------------Thu Feb 19 21:01:09 2015---------------
folder opened

---------------Thu Feb 19 21:01:27 2015---------------
folder closed

Example output directory_log.txt:

---------------Thu Feb 19 21:14:24 2015---------------
+ /home/jacob/Afbeeldingen/Downloads/2023.pdf
- /home/jacob/Afbeeldingen/Downloads/History-journal
- /home/jacob/Afbeeldingen/Downloads/google-earth-stable_current_i386.deb

The script:

  • Set it up like the scripts above with an important difference:

    • instead of using the foldername as an argument, set the complete path+the foldername in the head of the script (see example in the script)
  • The command to run it then is:

    python3 /path/to/script.py
    
#!/usr/bin/env python3
import subprocess
import os
import time
import difflib
from threading import Thread
home = os.environ["HOME"]

# The folder to watch:
folder = "/home/jacob/Afbeeldingen"
# the path your log files are saved to (change if you want to, but use full paths):
path = home
#---

for f in os.listdir(path):
    if f.startswith("dr_check_"):
        os.remove(path+"/"+f)

dr_data = path+"/directory_log.txt"
access_data = path+"/access_log.txt"

for f in [dr_data, access_data]:
    if not os.path.exists(f):
        subprocess.Popen(["touch", f])       
foldername = folder.split("/")[-1]

def check_windowlist(foldername):
    while True:
        try:
            if foldername in subprocess.check_output(["wmctrl", "-l"]).decode("utf-8"):
                return "folder opened\n"
            else:
                return "folder closed\n"
            break
        except subprocess.CalledProcessError:
            pass

def check_directory(directory, outfile):
    with open(outfile, "wt") as out:
        for root, dirs, files in os.walk(directory):
            for f in files:
                out.write(root+"/"+f+"\n")

def run_accesscheck():
    while True:
        ch1 = check_windowlist(foldername)
        time.sleep(0.5)
        ch2 = check_windowlist(foldername)
        if ch1 != ch2:
            with open(access_data, "a") as out:
                out.write("-"*15+time.ctime()+"-"*15+"\n"+ch2+"\n")

def run_directorycheck():
    last = 1; outfile_name = "dr_check_"; last_outfile = ""
    while True:
        outfile = path+"/"+outfile_name+str(last)+".txt"
        check_directory(folder, outfile)
        if last != 1:
            changes = []
            diff = difflib.ndiff(
                open(last_outfile).readlines(),
                open(outfile).readlines()
                )
            for item in diff:
                if item.startswith("-") or item.startswith("+"):
                    changes.append(item)
            if len(changes) > 0:
                with open(dr_data, "a") as out:
                    out.write("-"*15+time.ctime()+"-"*15+"\n")
                    for it in sorted(changes):
                        out.write(it)
                    out.write("\n")
            os.remove(last_outfile)       
        last_outfile = outfile; last = last+1
        time.sleep(600)

Thread(target = run_directorycheck).start()
Thread(target = run_accesscheck).start()

If you want to use Bash instead Python:

#!/bin/bash
folder=$1
while true;
do
    command=$(wmctrl -l | grep -o "$folder")
    if [[ "$folder" == "$command" ]];
    then
        ./myscript.sh
        break;
    fi
done

Edit:

I changed a script so you can run it with the following command:

bash folderwatch.sh BackupSSD

Also, you can make a script executable so you can use it without sh or bash, because the shell is defined in the first line of the script, for example:

chmod u+x folderwatch.sh
./folderwatch.sh BackupSSD