How I use remind(1)
Overview
The
remind
calendar utility offers an amazing degree of power
for doing calendar-related scheduling unmatched by
any other calendar program I've used.
If you haven't encountered it before,
this
presentation (PDF)
gives a good overview of some of the features,
as do
Linux Journal
and
43 Folders.
In this post,
I'll walk through
how I use both the basic
and advanced features.
This post makes a couple assumptions about you as the reader:
- General working knowledge of the CLI
-
While most of the shell commands
should work in the majority of Unix-like shells
(
/bin/sh
,bash
,ksh
,zsh
, orcsh
/tcsh
) some have slight syntax differences. Such differences might include thefor
loop syntax (csh
/tcsh
has a different syntax than thesh
style loops I use), and what variables the shell provides (such as$COLUMNS
thatbash
&ksh
provide, which you might have to define or hard-code before using in other shells). If you build from source into a local~/bin/
directory, you will want to know about modifying your$PATH
as well. You don't need a Ph.D. in using the command-line to do any of these, but I do assume basic directory creation & navigation skills usingmkdir
&cd
, as well as moving/renaming and copying withmv
/cp
and output redirection. I also have a section on usinggit
orrcs
to version your reminders but that section aims to help you integrate whatever version-control software you already use rather than try to teach either as a requirement. - General competency with a text editor
-
Reminder files are pure plain-text.
So you can edit them with your favorite
$EDITOR
, whethernano
,emacs
/mg
,vi
/vim
/neovim
,ed
,cat
, a magnetized needle and a steady hand, or butterflies. But this guide assumes that you can edit text files and save them. -
You have
remind
already installed or can install it -
If you administer your personal system,
you should have
root/
sudo
/doas
access to installremind
from a binary package (available in most repositories) or possibly install a C compiler likegcc
/clang
to install from source. If you do not administer your system, you can either talk to your system administrator and ask them to installremind
(try offering baked goods); or you can install from source, specifying a user-local directory such as$HOME/bin/
(as long as you include that in your$PATH
, you have a C compiler on the system, and your administrator didn't mark your$HOME/
directory's mount-point asnoexec
).
Installing
As a package
Initially released in 1990
via USENET on
comp.sources.misc
,
nearly every package repository
should provide
remind
,
making it as easy to install
as any other package:
On some systems,
this has no dependencies;
on other systems,
this brings in some
GUI
components
(Tcl/tk)
because the package might also include
the optional graphical tkRemind
component.
From source
Alternatively, download the source
directly from the
latest tarball
(and hopefully verify it using
gpg
with the
PGP key
which should have the fingerprint
738E:4D95:4052:902C:147D:07B2:685A:5A5E:511D:30E2
)
or if you want to download from
git
:
and then compile it yourself:
If you prefer not to
make install
you can move/copy the resulting binaries from
remind/src/{remind,rem2ps}
to a
~/bin/
directory
(in your
$PATH
),
create a link from
remind
to
rem
,
and put the
man
pages from
remind/man/*.1
in your
$MANPATH
.
Getting started
Creating your first
remind
file
By default, invoking
rem
expects data in
~/.reminders
and you can just dump all your reminders in there
(though
remind
also lets you
split reminders up into
individual files).
When invoked as
remind
rather than
rem
you must specify the reminder file directly:
The most basic reminder
consists of
REM
followed by a date of some sort,
followed by
MSG
and the text of the reminder.
A few things to keep in mind
-
remind
ignores blank lines. and treats lines beginning with a#
as comments -
to continue a reminder on the next line,
end the previous line with a backslash
("
\
") -
If you only include part of the date
remind
assumes the missing parts repeat. - You can specify either the numeric day of the month or the day of the week (in which case it will repeat on that day of the week).
-
For the month and week-day,
remind
does not distinguish between upper/lower-case and thus treats "Jan", "jan", "JaN", "jAn", and "JAN" all as the same. -
Additionally, while
remind
requires the first three characters of months ("Jan") and weekday names ("Wed"), you may also fully spell out the month ("January") or weekday name ("Wednesday") if that makes it easier for you to read. -
If you provide more than one weekday,
remind
will match any of them. -
Alternatively, you can write the date in
YYYY-MM-DD
format -
If you omit all of the values,
omit the
REM
too
For example if you use your
%b
- described above, producing "in N days' time", "tomorrow", and "today"
%l
- replaced with "on YYYY-MM-DD", "tomorrow", and "today"
%c
- replaced with the day-of-the-week of the event such as "on Wednesday", then "tomorrow", and "today"
%2
-
replaced with the
AT
time in 12-hour am/pm time, e.g. "at 2:30pm"
$EDITOR
to create this
~/.reminders
file
To see (most of) these reminders,
use either of these commands
If you happen to run the command
on January 1st, 2020,
you can omit the date
because
remind
defaults to the current date.
I most frequently invoke just
to get today's events.
By specifying a day of the week
and a numeric date,
remind
will find the next date-match
and will then scan forward
until it finds the matching day of the week.
Beware
with that "Mon 29"
because the
"Monday on-or-after the 29th"
might fall in the following month.
When we discuss the
SATISFY
clause later,
we can limit this
so it only notifies
on the fifth day
in the
same
month.
Also note that this can have interesting side-effects such as trying to find Labor Day.
Similarly, our city offers a
program for recycling items
that we shouldn't put in our recycle bin
(hazardous household waste,
electronics, etc).
These events occur quarterly
on the Saturday
after the first Friday
of the month.
We can express the date as
to ensure that it falls on the Saturday after
the first Friday in the month.
We can then add the
SATISFY
(discussed later)
to ensure it only falls on one of the quarterly dates:
Adding time information
To indicate the time and length of an event use
AT
(in military/24-hr time format;
I wish the parser allowed
for 12-hr AM/PM notation, but alas)
and optional
DURATION
(in hours and minutes).
Here I have a doctor's appointment at 2:30pm
but don't know how long it will run.
And I go to the gym
on Tuesday/Thursday
at 5:00am
for an hour and fifteen minutes:
At the moment,
adding time information doesn't
net you any big gains in the agenda view,
but they will appear in the
week/month view
and when we discuss
substitution filters
they let you include time information in the
MSG
.
Getting advance notice
Sometimes you want a bit of advanced warning
like knowing about your doctor's appointment tomorrow.
Perhaps you want a week of notice
before your mother's birthday
so you can get something in the mail.
Or you want a 60 days of notice
before you have to pay taxes
so you can get all your paperwork in order.
remind
uses
+n
and
++n
to give you
n
days of advanced warning.
The single
+
skips over
OMIT
dates,
while
++
ignores
OMIT
dates.
For now, use the
++n
form for simplicity.
remind
recognizes that you likely want this advanced notice
in the agenda view
but smartly prevents the advanced noticed from cluttering up
in the ASCII
week or month calendar views
(unless you explicitly request them with
-pa
or
-sa
).
While less useful to get the same reminder several days out,
remind
lets you
adjust the reminder text
based on the number of days out
to make these more helpful.
Adjusting dates
To adjust a date backwards, use
-n
or
--n
to specify the number of days.
At first glance,
this provides little benefit
over just doing the math
and hard-coding it
for normal reminders:
However, this lets you schedule reminders
relative to the last day of the month:
The first one
finds the first day of the month
and then goes back 1 day.
The second entry
goes back 7 days from the first of each month.
The third entry
finds the first Wednesday in the month
and then goes back 7 days
(to the last Wednesday of the previous month).
The last entry does similarly,
finding the first Wednesday in February,
then going back 7 days
to find the last Wednesday in January.
As with
+n
vs.
++n
,
the single
"-
"
jumps over
OMIT
dates,
(so they can end up going back
more than n days
if they encounter an intervening holiday);
while the double
"--
"
ignores
OMIT
dates.
These adjustments can combine
This will put the event on the last day of the month
but also remind you 3 days in advance
that the end of the month will arrive soon.
Repeating reminders
As shown above,
if you omit portions of a
REM
statement,
remind
treats the missing date part
as a repeat.
This lets you create reminders
such as
birthdays and anniversaries,
holidays,
weekly events like church,
or invoicing customers
on a given day of each month:
Note the "++7" style notation
to get a week of notice before my parents' anniversary
so I get something in the mail for them.
I also get a warning one day before April Fool's Day
so I don't get surprised by pranks.
On occasion I need a reminder every N days like picking up a 90-day prescription that started on January 27th of 2019. To do this, use an asterisk followed by the number of days to repeat. I'll cover a more complex example of a medication schedule towards the end.
For repeating events,
remind
lets you specify date ranges
using
FROM
,
UNTIL
,
and
THROUGH
keywords.
For example, if your vacation starts on December 21st
and repeats daily
(*1
)
until January 1st
you can use:
The
THROUGH
keyword simplifies the common
"*1
"
use case,
so this works exactly the same:
You might also have to take a medication
every other day
(*2
)
starting on January 1st
through the end of the month:
Alternatively, you might have a
Wednesday game night during the school year,
or a meeting on the 12th
of each month during the school year:
Modifying the output
Other views
If you have no reminders for a given day,
remind
prints "No reminders."
Otherwise,
remind
prints a banner
of the form
"Reminders for Wednesday, 1st January, 2020 (today)"
followed by an agenda-view of the reminders for that day.
If you want more than one day worth,
specify the number of days with
"*n
".
Note that you should quote the "*"
either by escaping it with a backslash
or by wrapping it in quotes.
Otherwise the shell might expand it
to a filename in your current directory.
To show four days of reminders:
remind
also offers an ASCII calendar view
for either the month or week:
You can change the default width
for either the week or month view
by adding
-w$COLUMNS
As of version 3.3.0,
remind
automatically detects the screen width
if you output to a
TTY
so you no longer need to specify this explicitly
unless you want something other than the current screen width.
Other message-types
Up to this point,
we've only discussed the
MSG
message-type.
remind
also provides
CAL
for events that should only show up
in the
week/month view
but not the agenda view, and
MSF
which behaves the same as the
MSG
except it wraps long text
in the agenda view.
(remind
provides a couple other reminder-types:
RUN
for running an external command,
SPECIAL
,
for conveying out-of-band information,
and
PS
/PSFILE
for passing PostScript information
to a PostScript-processing back-end
which I'll skip for now).
Getting rid of blank lines
I find it annoying
to get a blank line after each reminder.
You can prevent this
by ending reminder text with a
%
For simplicity,
I've omitted the trailing
%
from all of the other examples in this post,
but I put it on
every single reminder.
Selectively showing text
You might want
certain text to appear
on the daily agenda calendar
but you don't need that level of detail
on the
week/month calendar view.
You can wrap the text you want
on the week/month calendar view with
%"
…%"
With this in place,
it will display
the full text of the reminder
(without the
%"
markers)
on the agenda view
but only show
"Doctor"
on the visual calendars.
Substitution filter
Your reminder-message can include escape sequences
(beginning with a
%
)
that adjust based on the context.
So instead of a reminder that just reads
"Mom's birthday"
you can use
and
remind
will display
"Mom's birthday
in 7 days' time",
…,
"Mom's birthday
in 2 days' time",
"Mom's birthday
tomorrow",
and finally
"Mom's birthday
today".
The
man
page for
remind
has a whole collection of these
under the
"SUBSTITUTION FILTER"
section.
I most frequently use
Tweaking the banner
remind
prints a "banner"
before each day's agenda.
The default
BANNER
contains
"Reminders for %w, %d%s %m, %y%o:
"
but you can use any of the
substitution filter
formatting sequences that fit your wants.
The extra blank line after the
BANNER
bothers me so let's get rid of that
by putting a
%
at the end:
and I prefer the month presented before the day:
When I use
rem '*3'
to
get 3 days worth of reminders
I dislike how it formats the dividers
because they all flow together visually,
making it hard for me to read.
Fortunately,
remind
lets you use an
IF
/ELSE
/ENDIF
block to conditionally
tweak this as well:
On the first pass,
subsequent_iteration
is not defined,
so we set the default
BANNER
and then set/define
subsequent_iteration
.
The
PRESERVE
keyword then keeps the value of
subsequent_iteration
defined through subsequent iterations.
Now defined,
remind
takes the
ELSE
block,
prefixing the banner with a row of dashes,
a new-line,
and then the normal banner text.
Sorting
By default,
remind
prints agenda items in the order in which
they appear in the reminder files.
Using the
-g
flag tells
remind
to sort the output.
Following the
-g
comes up to four
"a" or "d"
characters to indicate
ascending
or
descending
order.
Positionally, these indicate
- the trigger date of the event
- the trigger time of the event
- the priority of the event
- whether to sort timed events before untimed events
rem -gaad
sorts first by trigger date (ascending),
then by trigger time (ascending),
and finally by priority (descending),
while not specifying whether
timed or untimed events should come first.
Sub-BANNERs for sorting
When sorting reminders,
I like to have a visual indication
between where today's reminders stop
and subsequent days'
advanced notices
so I override the special
sortbanner()
function.
Much like the
BANNER
,
this lets you define
a separator that comes
between these events.
I use the
$SortByDate
,
$SortByPrio
,
and
$SortByTime
variables to determine if any
sort-order
creates these logical breaks,
and then use a
substitution filter
in my function-result:
I use the
BANNER %
to suppress the regular banner,
letting the
sortbanner()
assume those duties.
Also, by capitalizing the substitution-filter
(%B
instead of %b
)
it capitalizes the resulting output.
Next instance of reminders
If you want to know the next time
a reminder will occur,
remind
offers the "next"
(-n
)
flag.
Beware as this produces ~900 reminders for me
(one for each of my 1600+
reminders yet to happen in the future),
but you can
grep
this output or pipe it to
$PAGER
File management
Organizing reminders
While
remind
will happily let you dump all your reminders in
~/.reminders
,
it also provides functionality
to split and combine files using the
INCLUDE
directive.
I like to organize reminders
into files based on reminder-type,
so I created a directory to house them all
and link the
~/.reminders
to point to my main reminder file:
This uses a little
remind
magic
(see the section on
expressions
below)
to dynamically find your reminder directory
for later use in the
INCLUDE
statements.
This lets me move
or
rsync
my reminders to another machine
under a different
$HOME
and everything will continue to work.
Alternatively,
you can create a link
to the containing folder
instead of a reminder file
full of
INCLUDE
lines:
and
remind
will process all of the
*.rem
files in the target folder
in glob order.
If you want a particular order,
you can name the files
with a prefix like
010-birthdays.rem
,
015-anniversaries.rem
,
020-work.rem
,
etc.
Initial file types
I start by creating a
helpers.rem
file to store a variety of
helper functions and constants.
I then create files for various event types and
INCLUDE
them into the master
reminders.rem
file:
While I've simplified my list of calendars above
for the sake of brevity,
I have nearly 30 different calendar files
to keep things organized:
- an individual file for myself
- one for my wife
- one for each kid
- one for each kid's school calendar
- birthdays
- anniversaries
- family events
- household chores
- finances
- church events
- one for each place I volunteer
- US holidays
- events at our local library
- work
man
-page-per-day and
a schedule of when the Cowboys play
(I don't care much about football,
but Dunkin offers a free medium coffee
on the day following a Cowboys win
so my calendar consists of reminders for the day
following
a game to check the score).
I also have a couple calendars
consisting only of
INCLUDE
statements to combine other calendars:
- one for the kids (combining both kids' personal calendars and their school calendars)
- one for the whole family (combining my calendar and my wife's calendar with the combined kids' calendar)
-
and of course the master
reminders.rem
that contains everything
At first glance, splitting out each file doesn't seem to offer any advantages. However, this lets you do things like show a calendar of birthdays only without all the other reminders, or show just a school calendar.
The majority of my
ushol.rem
file comes from the
examples on the
remind
website.
It creates a bunch of
OMIT
entries useful
for any reminders
that get bumped because of US holidays.
Advanced features
While all that is useful,
other calendar programs like
Google Calendar,
Microsoft Outlook,
or even the venerable
calendar(1)
CLI utility
can do many of those things.
However
remind
really shines when you want to do things
that these others simply can't.
Expression evaluation
For even more power & flexibility,
remind
lets you
define variables & functions
as well as
evaluate expressions
(documented in the man-pages as
"EXPRESSION PASTING").
To set a variable, use
SET
and
remind
will evaluate the right-hand side
(the
"4 - 3
"
in the example),
assigning the result
to the variable on the left-hand side
(the
myvar
in the example):
To create a function, use
FSET
.
The example below creates a function named
add_three
that takes one parameter named
x
.
When called,
it adds three to the number passed to it
and returns the result:
To evaluate an expression
put it in square brackets
(all of these produce the same output)
Note that you can use
an expression anywhere that a
number,
string,
or date/time
can appear.
This lets you do things like
Which lets you easily change
the vacation start-date
and the length of the vacation,
automatically adjusting the items
relative to those dates
(even moving relevant messages
off of weekends thanks to
OMIT
functionality).
Using functions in reminder text
If I know the birthday or anniversary of a friend,
I like to include that in the reminder text.
So in my
helpers.rem
I have
This lets me cleanly annotate things like
which produces output like
(ignore the date discrepancies)
Days until an annual event
As an example of function use, say you want to determine the number of days until an annual event. If it has already passed this year, you want to refer to the event next year; if it hasn't already passed this year, you want to refer to the event this year. A function wraps up this functionality nicely:
When run, this will produce output
Specifying the priority of a reminder
While I don't currently use task priority much,
remind
allows you to provide a
PRIORITY
modifier (an integer between 0 and 9999)
for an event.
If unspecified,
remind
sets the priority to
$DefaultPrio
(5000 by default).
As far as I can tell,
there's no inherent meaning to the priority,
so if it makes more sense to you that
9999 means "high priority" and
0 means "low priority",
then use them as such;
if it makes more sense to you that
0 means "high priority" and
9999 means "low priority",
use that.
I tend to declare a couple constants using
SET
and then use those,
making it easier to reverse the meaning
should I need to.
Adding a prefix/suffix to agenda items
remind
provides a pair of special functions,
msgprefix(p)
and
msgsuffix(p)
,
that emit a prefix & suffix
around each agenda item.
These functions take an explicit
priority
parameter
(although you can reference other variables and functions)
allowing you to do things like
(assuming higher numbers mean higher priority;
otherwise, adjust the
is_low_prio
and
is_high_prio
functions accordingly)
or, using some of the
tricks from the section on color
Omitting and shifting dates
Some events shift around
or get cancelled
based on whether they fall
on particular days like holidays,
weekends,
or even just for arbitrary reasons
("somebody else booked the conference room
so we need to move it to next week").
The
OMIT
keyword tells
remind
how to identify these cases.
Omitting/shifting for a single event
You might have a project at work
that runs for a couple weeks
but you don't want your agenda
to display it on weekends
because you have work/life boundaries.
Use the
OMIT
keyword to tell which days, and
SKIP
to tell
remind
to ignore events that fall on these days.
Put them in your
REM
command:
In another case,
your employer might pay you
on the 15th
of each month,
but if pay-day falls on a weekend,
payment moves
AFTER
the weekend
to the following Monday:
Or you might have a bill
that you need to pay by the 15th
but if that falls on a weekend
you must pay it
BEFORE
the weekend
so that payment arrives on time:
Omitting/shifting for multiple events
Certain events such as federal holidays
can displace multiple reminders.
Rather than try and
OMIT
every single holiday
in every single reminder,
remind
provides bare
OMIT
entries similar to
REM
but they get added to a
contextual
omit-list.
If you have important weekly meetings
on Monday and Friday
and an unimportant meeting on Wednesday,
the following will bump the important Monday meeting
back to Tuesday if Christmas fell on Monday,
will bump the important Friday meeting back to Thursday
if Christmas fell on a Friday,
and cancel the unimportant Wednesday meeting
if Christmas fell on a Wednesday,
In 2015, Christmas fell on a Friday,
in 2017, Christmas fell on a Monday,
and in 2019, Christmas fell on a Wednesday.
The first command shows
that the Friday meeting
got pushed back to Thursday,
while the second command shows
that the important Monday meeting
got pushed forward to Tuesday
and the unimportant Wednesday meeting
simply vanished.
The same thing happens
around July 4th
since we have multiple
OMIT
entries.
Omit notice and adjusting backwards
As discussed in the
advanced-notice
and
adjusting-dates
sections,
remind
can give you advanced notice of reminders
and adjust dates backwards a certain number of days.
In that section,
I recommended using the double
++
/--
because they ignore
OMIT
dates, making them easier to understand.
However sometimes you want extra days' notice
if intervening
OMIT
holidays happens
(such as closing the bank or post-office)
in which case
you should use the single
+
form.
Likewise, use
-n
to find the last day of the month,
but you want it to skip over
OMIT
dates.
Omit contexts
Because not all places or calendars
use the same list of holidays
to determine
OMIT
days,
remind
lets you create custom
OMIT
contexts with
PUSH
(ending those temporary contexts with
POP
)
and optionally use
CLEAR
to temporarily remove previous
OMIT
dates.
As an example,
our kids' schools close for (most) federal holidays
but each has its own list of other days off
such as in-service days
and bad-weather make-up days.
For some calendars,
I want to totally ignore the global holiday list
and use custom
OMIT
lists instead.
For example,
a group of guys get together for breakfast
on the first Saturday of each month.
But a couple times
someone else has booked our venue,
pushing it back a week.
A couple things to notice:
-
the
CLEAR
inside thePUSH
/POP
block means that my globalOMIT
list of holidays doesn't impact the breakfast dates -
if future events bump the event
I only need to add them to the
OMIT
list at the top -
by having a local
OMIT
list as well (consisting of the non-Saturdays) theAFTER
postpones to the following Saturday rather than pushing it to the next day (Sunday)
Frankly, as a guideline,
I lean towards putting
every
OMIT
inside its own
PUSH
/CLEAR
/POP
block.
Omit function
On occasion you might find it easier
to describe the days you want to
OMIT
with a function.
remind
lets you create a such a function
that takes a date as the only argument,
and returns 0
if it should consider the date,
or returns non-0 if
it should treat the date as omitted.
Use the
OMITFUNC
modifier to tell
remind
to ignore the local
and global
OMIT
s
and use the
OMITFUNC
instead.
If you want to take the global
OMIT
list into consideration
or omit certain days of the week
like you might with a local
OMIT
,
use the
isomitted()
function in your
OMITFUNC
(the
moonphase()
function takes a date
and returns a number from 0 to 359
with 0="new moon"
and 180="full moon",
so the
near_a_new_moon
function returns true
within ~20° on either side
of a new moon)
Using
OMIT
for more complex dates
In certain cases
you might want to create an
OMIT
that falls on certain days
that otherwise work with regular reminders.
However if you try to do something like
remind
considers the "Mon" part of the
OMIT
a syntax error.
To get around this,
create it as a regular reminder first,
then use the resulting
trigdate()
to create the
OMIT
:
However, note that
this might have surprising side-effects
if you don't take care where you start your search.
Using
SATISFY
to conditionally test events
Sometimes you want
remind
to perform additional tests on a date
to determine whether an event should occur,
and if not,
continue seeking the next matching date
and test that.
The
SATISFY
clause lets you
evaluate an expression
to do just that.
You might only want something to happen every Nth month
or test that something only occurs on certain days.
As previously suggested
when discussing the Nth weekday of a month
you might need to check that the date
falls in the same month.
-
The first three reminders
trigger on the first of each month.
However
remind
will test the month-number ($Um
), dividing it by 3 and getting the remainder ("%
" does division and results in the modulus/remainder). If the remainder is 1, we have January/April/July/October; if the remainder is 2, we have February/May/August/November; if the remainder is 0, we have March/June/September/December. -
The second two reminders test
if the currently-considered date
(
today()
, the same as$U
) matches the real-world date (realtoday()
) and only displays theMSG
portion today. -
To find Friday the 13th,
we can't use
REM Fri 13
because this finds the first Friday on or after the 13th of each month. That might or might not coincide with the 13th. Instead, we find all the 13ths and then use theSATISFY
clause to reject those that don't fall on a Friday (0=Sunday, …, 5=Friday, 6=Saturday) -
To find the 5th Monday
we need to use the same procedure
for finding the other Nth
weekdays of the month
(
Mon 29
) but then check that the month of the resulting date ($Um
) falls in the same month (monnum()
) as the date seven days before (-7
) today's date ($U
, the date currently under consideration). -
In the final pair of entries,
I have a club meeting
every Monday night
from August through May.
However, club takes off during Christmas break
(and some other days I've not included here),
so the
OMIT
takes care of this. However, I don't want notifications on every day during the break telling me not to attend club. I only want notifications on the Mondays during the break. Using theSATISFY
clause lets me limit these so they only fall on Mondays ($Uw == 1
).
Fine-grained scheduling
Sometimes you want
advanced warning
of an event at certain intervals like
++n
provides,
but you don't want those warnings
on every day during that interval.
The
WARN
modifier lets you provide a function
that tells
remind
which days you want reminders.
The function gets called repeatedly
first with 1,
then with 2,
etc.
The search stops if one of the following conditions occur:
-
remind
encounters the current number of days out for the next instance of this reminder - it encounters a zero (today)
- the function results cease decreasing monotonically (they must continue decreasing, so "10, 5, 7, 0" will bail at 7 because it went up from 5)
choose()
function does this,
so to get reminders
30 days out,
10 days out,
5 days out,
and 2 days out:
When checking if
remind
should produce a reminder for today,
it starts by calling this
mywarnfunc
function with a parameter of 1.
The
choose()
function returns 30,
so if January 1st, 2020
is 30 days from the current date in consideration,
it will remind.
If not,
remind
calls
mywarnfunc
with a parameter of 2.
This time, the
choose()
returns 10.
Again if January 1st, 2020
is 10 days from the current date,
you will get the reminder.
Same with 5 and 2 days out.
Finally, if none of the results
match,
remind
tests if the date in question
is January 1st, 2020.
If you want to skip
OMIT
days, return a negative number.
The check for monotonically decreasing numbers
only tests the absolute value so a function like
works and will warn
30 days out,
10 days out
(skipping over
OMIT
days),
5 days out,
and 2 days out
(also skipping over
OMIT
days).
remind
provides a similar
SCHED
functionality for fine-grained
lead time for
AT
-style
reminders,
notifying at a series of times:
to remind you at
4 hours out,
2 hours out,
30 minutes out,
and at the time of your appointment.
You can read more under
PRECISE SCHEDULING
in the
man
-pages.
Miscellaneous tricks
As I've built up my
remind
files, I've developed a collection
of tricks and tips
that I use for various scenarios.
Labels
Because I have
a file for each type of reminder,
I find it handy to prefix each item with its filename
using the special
msgprefix(x)
function
(which I also
use for colorizing agenda reminders):
This produces
If you don't put your reminders
in separate files,
you could achieve the same effect
in a single file
with a variable and a function
that references it
as part of the
msgprefix(x)
function:
Color
As a rule,
I generally want plain-text reminders,
whether emailing them to myself
or displaying a calendar with
rem -c
.
But when at the command-line,
I like a bit of color.
General per-reminder foreground color
As one of the
SPECIAL
codes,
remind
offers
COLOR
(or it will happily accept
"COLOUR
")
for reminders
that alters the color used
when generating a
week/month calendar with
-cc+
or
-cc
.
The entry takes three numbers as arguments,
the
Red,
Green, and Blue values,
each ranging from 0 to 255,
followed directly by the
MSG
-style text
(without the
MSG
keyword):
When displayed on a traditional ANSI terminal
those get quantized into sixteen colors
(eight basic colors plus eight bold/bright versions of each).
This makes it easy to colorize
a single reminder,
but it only works in week/month view
(or external apps that support it
via the
-p
option),
not the agenda view
(unless you have version 3.3.0 or later
where the
-@
option enables color in the agenda view).
However sometimes I want to colorize
whole swaths of reminders
and do it on the agenda view.
I want all my birthday
and anniversary reminders
in green,
my personal reminders in blue,
my wife's reminders in bright magenta,
the kids' reminders in cyan,
church/volunteering reminders in magenta,
work in yellow,
and some low-priority entries in gray.
I don't want to have to specify
the color for each and every one
of my 1,600+ reminders.
Prior to the addition of
$DefaultColor
in version 3.3.0
To do this,
I first add a number of
ANSI escape codes
as constants to my
helpers.rem
that I can then use elsewhere.
Then, in my base
reminders.rem
file,
I have the following:
This checks if I've defined
COLOR
on the command-line
and sets up functions
& constants
for default colors.
I then set a color at the top of each of my files
after including the
helpers.rem
and it changes the color of everything following,
allowing me to have multiple color blocks
in the same file
The
msgprefix()
and
msgsuffix()
functions have special meaning to
remind
allowing the above to
- set the prefix color for the labels
- determine and display the file-name-based prefix
-
switch to whatever color
calcolor
currently contains - display the reminder text
- reset back to a normal color
I can then use
rem -iCOLOR=1
to get my results in color.
(ignore the fact that the events actually fall on different dates)
These work wonderfully for the agenda view,
but unfortunately don't work for the week/month view
(attempting to hack it with the special
calprefix()
and
calsuffix()
functions doesn't work either)
After the addition of
$DefaultColor
in version 3.3.0
I submitted a patch that has
(with modifications)
made it into the mainline codebase
in version 3.3.0,
released .
This patch allows you to set a
$DefaultColor
variable combining the benefits of the
COLOR
keyword
(appearing on the week/month calendars
and getting passed to external utilities)
with the benefits of my
calcolor
/msgprefix()
/msgsuffix()
trick above
(colorizing the agenda view,
without needing to specify the color
for every single event).
As an added bonus in 3.3.0,
if you invoke
remind
with the
-@
parameter,
it will colorize agenda items for you
in addition to the week/month calendar views.
Raw output
While pretty output works well for humans,
sometimes I desire to pipe output to another program.
I have no interest in trying to parse the
week or month calendar views.
The agenda view can serve
as a primitive & fragile output format
for post-processing.
However,
remind
provides the
-p
and
-s
options to produce machine-readable output.
This output format works much better
with tools such as
grep
,
sed
,
awk
,
and
python
/perl
/ruby
,
as well as the
tkremind
program.
remind
also allows your reminders
to convey additional metadata
to these back-ends.
Given this reminder:
Remind will output as follows:
The
-s
offers a simplified format
of only the calendar data:
- The date in YYYY/MM/DD format
-
The
SPECIAL
type (in this caseCOLOR
) -
a comma-separated list of
TAG
s associated with the reminder - the duration in minutes
- the start-time in minutes-from-midnight (510 = 60 minutes * 8 hours)
-
the
$RED
,$GREEN
, and$BLUE
values from theCOLOR
(255, 128, and 0 here) - the body of the message, prepended with the time (if present)
-p
option wraps it in the following meta-data:
-
a literal
# rem2ps begin
-
a line showing the month currently rendering,
the current year,
the current month,
the number of days in the month,
the week-day
(0=Sunday, 1=Monday, … 6=Saturday)
of the first day of the month,
and a boolean 0/1
based on whether the calendar
should display Monday first
(based on the
"
-m
" option passed toremind
) - the days of the week in the local language
- the next month and its number of days
- the previous month and its number of days
-
the list of reminders like
-s
produces -
a literal
# rem2ps end
# rem2ps end
indicates the end of one month
but the receiving-program
should handle the case
that more months' data could follow.
The date
(first column)
should always appear,
but the other fields might display an asterisk
("*
")
if not present in the reminder.
SPECIAL
reminders
While I don't give much space to them,
remind
's
SPECIAL
token lets you pass out-of-band information
to external back-ends.
This can include things like
changing the color of an entry
(with
SPECIAL COLOR $RED $GREEN $BLUE
),
shading the background of a calendar square
(with
SPECIAL SHADE $GRAYLEVEL
or
SPECIAL SHADE $RED $GREEN $BLUE
),
drawing the phase-of-the-moon
(with
SPECIAL MOON $PHASE
),
or adding
per-week
annotations
(with
SPECIAL WEEK $TEXT
).
I use
rem2ps
in the example regarding
relative dates
but don't employ any of the
SPECIAL
commands therein.
RUN
reminders
The
RUN
message-type acts much like a
MSG
message-type except that
after passing the body through the
substitution filter
it executes the resulting command
instead of displaying the result.
Beware of quoting and escaping issues.
While you can do things like
the shell will complain on certain days because
it lacks a closing single-quote trying to run
However, you could do something like
which would run
The
ONCE
keyword will only run the reminder once,
even if you invoke
remind
more than once in a given day.
However, it relies on the
atime
(most recent access-time)
of the reminder file.
Since my
/etc/fstab
sets
noatime
on all of my mount-points,
any reminders marked with
ONCE
run every time.
I keep a list of "to-do" items so I tried including those in my reminders: to output the top 5 things on my to-do list.
I also experimented
with keeping larger stretches of prose
in an external file like
and then using
RUN
to emit the content
However, I found these of limited use,
so I don't really use
RUN
in my day-to-day.
Tagging
Similar to the
SPECIAL
keyword, the
TAG
keyword lets you assign one or more tags
to your reminder.
While
remind
doesn't do anything with them directly,
it passes them through
to other back-ends
when using the
raw-output
modes.
Because we
write monthly letters
to our grandparents
I use this functionality to tag
newsworthy events in our calendar.
I can then use
the raw output of the
-s
and filter for only those items:
so I have a list of what we did during the month.
This works well in my reminders too,
using a
RUN
directive:
(the
-r
prevents
remind
from executing
RUN
entries further,
and the expression
[$U - 1]
finds the last day of the previous month
to pass to
remind
)
Thus, not only do I get a reminder
to let me know who writes the letter
but I get a list of the letter-worthy things
we did in the previous month.
I wish
remind
had more native functionality
to support tags
such as a
trigtags()
function
(to return the tags
for the current reminder),
or a
hastag()
function
(to ask if the current reminder
has a given tag).
This would help me
filter output
or expand tags in message-bodies.
Alas, not yet.
I look forward to some day (hopefully)
when these are even more useful.
Perhaps for my next patch.
Shell tips & tricks
Aliases
I've set up a couple
bash
aliases
in my shell to minimize typing:
These let me type
3
as a command
and get 3 days
of colorized agenda output.
Crontab
I have
crontab
entries that email me my
today/tomorrow reminders each day
and a full week of reminders on Sunday morning:
Version control
I find it useful to keep my reminder directory,
~/.config/remind/
,
in version control.
I use
git
for multiple files,
but you could just as easily use
mercurial
,
Subversion
,
or even
CVS
or
rcs
.
This gives me a lot of freedom
to experiment with complex reminder syntax.
If I mess something up,
I can always revert to the known-good state.
Also, I can delete reminders that have passed
so they don't clutter my calendar
or increase processing time,
but still have historical record of their existence.
I can also
git push
&
git pull
changes between different machines
to keep my reminders in sync.
Speech
The readable nature of the output
makes it easy to pipe output to a program like
espeak
(or another text-to-speech engine
which you would also have to install)
to read your reminders aloud.
You might create a custom
spoken.rem
file that you don't
INCLUDE
in your regular
reminders.rem
in which you remove the
BANNER
,
emit section dividers
(if you don't choose to use
display labels),
and
INCLUDE
a select subset of calendars:
(the
[filedir()]
expands to the path of the containing
spoken.rem
file)
And to read them:
Easier or more boring recipes
Car registration
We need to get our car inspected
and get our (re)registration submitted
by the end of February
so I want notifications starting at the beginning of February:
The
Mar 1 --1
results in the last day of February
while the
++[26 + isleap($Uy)]
gives 26 or 27 days of advanced warning
depending on whether the current year
is a leap year.
Once I've done the registration/inspection,
I update the year in the
SATISFY
so it stops reminding me about it for that year.
Maturing bonds
I purchased a couple simple-interest bonds
and want to know when to expect the interest payment checks.
They arrive
around the 25th of the month
(or
AFTER
if it falls on a Sunday
when the USPS doesn't deliver mail),
every 6 months
until the end of the bond-term.
But
remind
makes this pretty easy:
This uses the
SATISFY
keyword,
repeatedly looking for the next
25th of the month
until
$Tm % 6 == 2
— the month in question
is either February or August.
Notice until action
I send birthday cards
to certain friends & family members
so I want about a week of notice
to get a card,
write a note,
and get it in the mail.
However, once I've sent the card,
I don't need further
advanced warning reminders.
By updating that year in the expression
once I've completed the task
I no longer get advanced warning for the given year.
Similar to the
car registration
example,
in this case I still want to receive notification
on the day
of
the birthday too,
not drop the reminder entirely.
I use a single
"+
"
for Grandma's notification
because sometimes holidays interfere with the mail
so I appreciate having the extended notice.
Messages tweaked based on date
As a Christmas gift to our grandparents, we take turns hand-writing a letter to them each month. So on the first of each month, I want a reminder, but also want to know whose turn: Similarly, if I scrub the tile grout in the kitchen one week, our master bathroom the next, and the kids' bathroom the third week I can achieve this with:
Selectively getting advanced notice
For friends I see at church on Sunday I would like advance notice of their birthday on Sunday but don't want additional reminders during the intervening days of the week. For this, I use an expression evaluation to dynamically determine the number of days worth of notice: On Sunday, January 12th, I get notice about Tony's birthday coming up on Thursday so I can wish him a happy birthday when I see him. And I get notice on Thursday, his actual birthday. But I don't have my agenda cluttered with extra advance warnings on Monday through Wednesday.
Annoying credit-reporting company rules
I learned the hard way that the credit-reporting companies will not let you order your credit report on the same day each year because a full year hasn't actually passed. Yes, I'd love to pull my free credit report on the same day of each year. However, I need to pull them more than 365 days (366 on a leap-year) apart. So I use the repeat modifier to get reminded every 367 days. I also intersperse them throughout the year rather than all at once so that I get a more frequent sampling in case something has gone wrong: The subsequent dates then drift forward automatically but I don't have to worry about trying again inside the one-year window of time. Also, I include the phone-number and the URL so I don't have to look it up every time.
Post-holiday candy sales
I tend to stock up on chocolate
when it goes on sale after some of the big candy holidays.
(I keep stashes of chocolate hidden around the house
so that when my beloved has a hankering for a certain type,
I can often play the miracle-worker
and bring it out of hiding).
So I use
remind
to make sure I don't forget:
The post-Easter one requires actual calculations
and
remind
does this without much thought at all
thanks to the built-in
easterdate()
function.
The rest wouldn't pose any trouble
for an ordinary calendar program.
Did any of these reminders trigger?
Sometimes you have several events
and you want to provide some
SPECIAL
modifier for all of them.
You can use
remind
's
special
$NumTrig
variable to check this:
If any of the events occurred
after we snapshot the
$NumTrig
,
then the
IF
will allow for the
SPECIAL SHADE
to happen for the day in question.
FizzBuzz
Often in interviews
the interviewer will instruct
the candidate to whiteboard the classic
fizzbuzz
solution where the program emits
n
numbers but
if the number is divisible by 3
print "fizz",
if the number is divisible by 5
print "buzz",
and if the number is divisible by 3 & 5
print "fizzbuzz"
(otherwise just printing the number).
So in case you ever need to do this using
remind
:
The
doy()
function returns the Julian day of the year
(as found by taking the current date,
$U
,
and subtracting December 31st
of the previous year)
and then uses that to calculate
the fizzbuzz-ness of the day.
Dumb? Yep.
Useless? You bet.
Fun demonstration? Why not.
More complex calendar recipes
Shifting garbage day
The go-to for demonstrating the power of
remind
involves shifting your garbage collection day back
if a holiday occurs earlier in the week:
Change the
garbday
value to your garbage day
(Monday = 1, Friday = 5)
and set up your holiday
OMIT
entries in your
holiday.rem
(or hard-code them inside the
PUSH
/CLEAR
/POP
block)
and
remind
will automatically shift the garbage day reminder
based on the holiday schedule.
Pretty date-diff
While
remind
lets you subtract two dates
to get the number of days between them,
sometimes you want that difference
expresses as a number of years, months, and days.
To do that,
I have this
ymd_diff
function which returns
values like
"3y 2m 11d"
or
"3y 1m 14d ago"
which you might find more useful:
Generating reminder files programmatically
One of my calendars gives me a daily
man
-page
to read based on my list of system
man
-pages
looking something like
Creating this manually would have taken a long time.
A little shell-script easily generated the file:
This finds all of the
man
files in
/usr/share/man/man[1678]/
removing the many
perl
-related pages I don't care about,
shuffles them so I get a nice variety,
then uses the
awk
script to transform them into a
remind
file.
Now I have several years worth
of daily man-pages to read.
If you have a pre-existing list of dates,
you can use the standard suite of Unix tools
to transform the list
into something that
remind
can use.
Relative date calculations for event-planning
I serve on the leadership team
for a local non-profit
that works to provide clothing-vouchers
to kids on the school free/reduced-lunch program.
However the calendar changes each year
based on certain fixed dates including
when the school counselors have their training before school starts,
when school starts,
and which weekends the shopping event(s) take(s) place.
I use
SET
to define the fixed-dates,
and calculate the other dates based off them.
(I have some
OMIT
and
BEFORE
in there too to jostle things around a bit)
I can then use
rem -p3 2019-08-01 | rem2ps | ps2pdf - calendar.pdf
to generate the calendar PDF
that I can email out to the rest of the leadership team.
Medicine schedule
One of our kids takes a medication that we receive in a 30-day supply. This means we need to pick it up at the pharmacy at least the day before to ensure uninterrupted dosing. The pharmacy occasionally needs a business day or two to get the medication in, so I like to drop off the prescription a couple days early. Which means I need to pick up the prescription at the doctor's office before that. Which means I need to call the doctor's office 72hr before that. And all of those dates can get shifted by weekends and holidays. On a less-powerful calendar, I'd schedule a reminder every 30 days and then hope I remember to take weekends and holidays into consideration.
On the other hand,
remind
makes this fairly straightforward to express.
While doing this,
I'd also like to get
nagging reminders
and advanced notice
if I haven't yet picked up the pills.
But once I have,
I don't want continued reminders.
First we'll begin
by creating an
OMIT
context to hold the holidays
that the pharmacy & doctor's office close.
With that in place,
we'll set up some variables
to know when we most recently
picked up the medication
and when we started taking the medication
as well as how many days worth of medication
we receive
and the number of days of lead-time
the doctor's office has requested:
Now we need to find the date
of the next time we run out of pills.
The
SATISFY 1
produces no output,
but sets the most recent trigger date.
Using the
trigdate()
function,
you can capture the date of the most recent event
for use in further calculations
and later reuse.
So those lines determine
the next day we'll run out of pills.
From here,
we know that we need to pick up the pills
the day before then
(possibly more than one day before
if there's an intervening holiday or weekend):
We evaluate the day when the
pills run out,
and then back it off by one day
("-1
").
Because this uses the single minus,
it will skip over
OMIT
days, including holidays
that close the pharmacy & doctor's office,
as well as Sundays when the pharmacy is closed.
The
SATISFY
ensures that,
if we have picked up the pills
on or after the date currently under consideration,
we don't bother reminding about this.
I also like a little advanced warning
(+1
).
In case life gets busy,
I have a little more room to
fit in a trip to the pharmacy.
Once we know when we need to pick up the pills,
we can capture that date
(again, with
trigdate()
)
and use it to determine
when we need to
pick up the prescription
and take it to the pharmacy
(the day before,
but the doctor's office
doesn't open for prescriptions
on weekends or over holidays
so it might get bumped back
further).
Then we need to create a reminder
based on the date we need to pick up the pills
and back it off by the number of days
that the doctor's office says
they need for lead-time
(72 hours):
Again, we use the
SATISFY
to ensure we only get this
if we haven't already picked up
the most recent batch of pills.
This produces a final series
of variables and reminders
that notify me at appropriate times
and do all the holiday/weekend math
so we don't accidentally end up
without medication:
Then each time I pick up the pills,
I update the
MostRecentlyGotPills
with the new start-date.
You can use the same template
adjusting the
MostRecentlyGotPills
,
MedStartDate
,
MedFreq
,
and
RXLeadTime
,
for your use case.
Scanning forward from an earlier date
Sometimes you have an
OMIT
that has happened recently
and would thus impact how
advanced notice with
+
and
adjusting dates with
BEFORE
/AFTER
/SKIP
calculations occur,
but because
remind
first determines the
OMIT
dates based on the "today" in question,
it might not adjust properly.
For example,
consider a floating holiday like Labor Day
(the first Monday in September):
If you run this on
it will fail to find the event
on today but will instead find the
next
instance on
.
By starting the search a week back using
SCANFROM
,
we encounter the right date:
Similarly, if you want to find both
the 4th of July
and the day on which it gets observed,
you might need to look backwards a couple days
to find the closest 4th:
(taken from the
defs.rem
)
School N-day cycles ignoring holidays & weekends
Sometimes you want to know the number of days
between two dates,
without counting holidays
or other
OMIT
dates.
This often shows up in school schedules
where a 4- or 6-day cycle
often rotates across the standard 5-day week.
The
nonomitted()
function lets you do this
with minimal fuss.
However, the start-date
(the first parameter)
must
be less-than or equal to the end-date or
remind
will produce an error.
Daemon mode
In addition to invoking
remind
manually or from scripts,
remind
also offers a "daemon mode"
that will run in the background
and fire off commands as
AT
events come around.
This will launch
remind
which will check for changes in your
~/.reminders
every 5 minutes.
If an
AT
passes,
remind
will spawn
espeak
passing the reminder-text
(shell-escaped)
in place of the
%s
so that the reminder text gets spoken.
Alternatively you could use
xmessage
or
zenity
to pop up a dialog
(beware that this can end up
creating lot of
xmessage
dialog boxes)
or use
notify-send
to display the reminder-text
as a notification:
Because of the composability
of commands,
you could create your own shell-script to
email reminders,
text them to your phone,
hit a web service,
send them to a printer,
or print your
MSG
contents on a 3d-printer.
Endless possibilities.
Your reminder file can use the
$Daemon
variable to test to see if
remind
is running in daemon mode:
When run in daemon mode,
this will prefix your message
with "Hey, you!"
to get your attention,
especially if you
use
espeak
to speak your reminders
Other utilities
Generating iCalendar/.ics
files
Great.
Now you have all your reminders
generating just the way you want.
How do you share them with other people?
Most other
cough*inferior*cough
calendar programs still support
.ics
format files to describe events.
You can use the
rem2ics
utility to convert
the output of
remind
into a
.ics
file that you can import
into Google Calendar,
Outlook,
or iCalendar.
Note:
While it will export all of your reminders
including repeating reminders,
it will not include enough meta-information
to preserve things like repeats,
so the events show up in the external calendar
as individual events.
If you have your local timezone exported
as an environment variable,
you don't need to set it explicitly,
but it gets copied into the resulting
reminders.ics
file verbatim,
and having the wrong timezone
can cause lots of problems.
Other external utilities
Outside the scope of this article
you'll find things like
tkremind
(a GUI front-end to
remind
)
and
wyrd
(a TUI front-end to
remind
).
I don't currently use either of these
so I'll leave those
for the reader to explore.
Troubleshooting & debugging
While the vast majority of my reminders pose no issues, occasionally one ends up complex enough that I want to make sure it really works like I expect it to. Because these really are code, I appreciate having some tools to debug them.
Echoing into
remind -
As a first line of debugging,
I usually try to isolate the single reminder
and then run
remind
with the date I want to test.
If you specify a filename of
"-
",
remind
will read reminders from
stdin
which lets you
echo
a single reminder
to test it:
Occasionally you need a couple lines
to thoroughly test,
in which case wrap the
echo
statements in parentheses:
Alternatively, sometimes you need to test multiple days
or months
Using a custom
.reminders
file
Sometimes you really do need a lengthier test case.
For these, I back-up my
~/.reminders
file,
create a new one afresh
or modify the existing one
test against that until I get it right.
I then snapshot the working reminders,
and restore my original
Finally, I use my
$EDITOR
to copy the working bits from
~/working_reminders
into my reminder file.
Alternatively, I also have my machines
configured with a "guest" user,
so sometimes it ends up faster
to log in as the "guest"
(who has no
~/.reminders
file of consequence),
blow away any previous test
~/.reminders
file,
and test there on an isolated case
without risk to my
real
reminders.
Showing LOTS of dates
Sometimes I find it easiest to
look at a spew of calendars
and eyeball the results.
I like to do this in
month view,
producing a whole year
and paging through the results with my
$PAGER
(less
in my case).
Alternatively, I will use the
-p
/-s
flags to dump
all the reminders in a easy-to-filter format.
Printing values in
MSG
output
In the same classic tradition of
printf()
debugging,
I also find it useful to see what
remind
thinks a variable holds:
Debugging flags
For really getting under the hood
remind
has a
-d
option that turns on additional debugging output.
It gets verbose quickly,
but invoking
turns on all the debugging flags.
The
man
-page
details each of those options,
but it has occasionally helped me
spot something I would have otherwise missed.
I find the
x
(tracing expression evaluation),
t
(tracing trigger-date computation),
and
v
(dumping all variables)
help the most.
Wrap up
When I first heard about
remind
it sounded interesting,
but I tried multiple times
before it finally clicked for me
(much like
git
which took me about three serious attempts
before I felt like I understood it).
But once I figured out that I could express things
that no other calendar program let me do,
I have trouble keeping my primary calendar
in anything else.
I use
rsync
and
git
to keep my calendar in sync
across multiple machines
without exposing my calendar info
to third-parties
like Google
or Microsoft.
Hopefully this post gives you the motivation
to evaluate it for your hardest calendar test-cases.
And if you come up with
strange questions,
use-cases,
or other recipes
for using
remind
feel free to contact me via
email,
on the
remind
mailing list,
or
over on Twitter
@gumnos
and I'll do my best
to help out.