How to run a script on each login or once a day if not run that day already?

Solution 1:

Set this as a startup application:

#!/bin/bash
# Based on /etc/cron.daily/apt

check_stamp()
{
    stamp="$1"
    interval="$2"

    if [ $interval -eq 0 ]; then
    echo "check_stamp: interval=0"
    # treat as no time has passed
        return 1
    fi

    if [ ! -f $stamp ]; then
    echo "check_stamp: missing time stamp file: $stamp."
    # treat as enough time has passed
        return 0
    fi

    # compare midnight today to midnight the day the stamp was updated
    stamp_file="$stamp"
    stamp=$(date --date=$(date -r $stamp_file --iso-8601) +%s 2>/dev/null)
    if [ "$?" != "0" ]; then
        # Due to some timezones returning 'invalid date' for midnight on
        # certain dates (eg America/Sao_Paulo), if date returns with error
        # remove the stamp file and return 0. See coreutils bug:
        # http://lists.gnu.org/archive/html/bug-coreutils/2007-09/msg00176.html
        rm -f "$stamp_file"
        return 0
    fi

    now=$(date --date=$(date --iso-8601) +%s 2>/dev/null)
    if [ "$?" != "0" ]; then
        # As above, due to some timezones returning 'invalid date' for midnight
        # on certain dates (eg America/Sao_Paulo), if date returns with error
        # return 0.
        return 0
    fi

    delta=$(($now-$stamp))

    # intervall is in days, convert to sec.
    interval=$(($interval*60*60*24))
    echo "check_stamp: interval=$interval, now=$now, stamp=$stamp, delta=$delta (sec)"

    # remove timestamps a day (or more) in the future and force re-check
    if [ $stamp -gt $(($now+86400)) ]; then
         echo "WARNING: file $stamp_file has a timestamp in the future: $stamp"
         rm -f "$stamp_file"
         return 0
    fi

    if [ $delta -ge $interval ]; then
        return 0
    fi

    return 1
}

update_stamp()
{
    stamp="$1"
    touch $stamp
}


STAMP=$1
INTERVAL=$2
export DISPLAY=:0.0

while true; do
    if check_stamp $STAMP $INTERVAL; then
        # Do whatever you want
        /path/to/your/script

        # Update stamp
        update_stamp $STAMP
    fi

    # Sleep 10 min
    sleep 600
done

Replace the /path/to/your/script (inside the while loop) with the path to your script.
Call this script passing first a path to a file that will serve as a stamp followed by an interval in days, for example scriptname /home/user/stampfile 1. It will run endlessly and check the current time against the stamp every 10 minutes (sleep 600). If the difference is greater than the interval it runs your script and updates the stamp.

Solution 2:

I created a script called runonce that runs the supplied command only once per 8 hours (configurable). I placed the script into my ~/bin. Then I simply use this script within my ~/.bashrc:

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# Upgrade my dotfiles but not always
runonce ~/.../... supi

# Print quote but not always
runonce myquote -s