Question

I would like to limit the number of executions of a command in a given time period. I know a way to do this, but my way is not neat and I would appreciate suggestions on better ways to accomplish this. Specifically, the scenario I'm dealing with is as follows:

I am using the program Motion to monitor and record images from a webcam. The program saves an image and executes a command whenever motion is detected. One of the commands I want it to execute is a simple command to send me an e-mail when motion is detected. A difficulty arises because this command can end up being executed multiple times per second. This can quickly result in thousands of e-mails being sent in a very short period of time. What I think I would like is a procedure such as the following:

on motion detected
 Has it been more than 1 minute since motion was last detected?
  If it has, send a notification e-mail.
  If it has not, don't send a notification e-mail.

I'd like to wrap up that procedure in one neat command. My current approach involves saving a temporary file, which I suspect is not the neatest way of doing things.

Thanks for any thoughts on this!

Was it helpful?

Solution

Well, here's the type of script I've got that runs each time motion is detected:

#!/bin/bash
    #user variables #userSet
        timeIntervalInSecondsBetweenCommandExecutions=120
        lastExecutionTimeFileName="last_command_execution_time.txt"
        command=$(cat << 2012-08-20T1654
twidge update "@<Twitter account> motion detected $(date "+%Y-%m-%dT%H%M")";
echo "motion detected" | festival --tts
2012-08-20T1654
)
    # Get the current time.
        currentTimeEpoch="$(date +%s)"
    # Check if the last execution time file exists. If it does not exist, run the command. If it does exist, check the time stored in it against the current time.
        if [ -e ${lastExecutionTimeFileName} ]; then
            lastCommandExecutionTimeEpoch="$(cat "${lastExecutionTimeFileName}")"
            timeInSecondsSinceLastCommandExecution="$(echo "${currentTimeEpoch}-${lastCommandExecutionTimeEpoch}" | bc)"
            # If the time since the last execution is greater than the time interval between command executions, execute the command and save the current time to the last execution time file.
                if [ ${timeInSecondsSinceLastCommandExecution} -ge ${timeIntervalInSecondsBetweenCommandExecutions} ]; then
                    eval ${command}
                    echo "${currentTimeEpoch}" > "${lastExecutionTimeFileName}"
                fi
        else
            eval ${command}
        fi

In a nutshell, it is using a file to remember when it last ran. So, it's an answer, but I still figure it's inelegant.

OTHER TIPS

The traditional approach is to create a file, using it to store a timestamp either in its content or via its metadata (mtime e.t.c.). There is no other standard way to have persistent information outside a process - I assume that you would consider databases and such to be overkill.

There may be an alternative however, if the caller (e.g. motion) blocks waiting for your process to finish. In that case, your script could look like this:

#!/bin/sh

echo "The Martians are coming!" | mail -s "Invasion" user@example.com

sleep 60

The last line ensures that any caller that waits for this script to terminate will have to wait for at least 60 seconds, which imposes a maximum rate limit.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top