Skip to content
Advertisement

How can I combine the commands leave and say?

Is there a way to make use of the command leave and the command say (MacOS) to be notified by the built in voice system?

I can do echo hello | say on the command line.

leave outputs Time to leave! how can this output be piped to say once it appears?

When I issue this command it’s just hanging. (no leave process is created)

# one minute from now
leave +0001
Alarm set for Fri Jun  3 15:55:05 CEST 2022. (pid 37692)
pgrep leave
37692
# triggers: Time to leave!

# one minute from now piped to say
# this one is just hanging there.... (no leave process created)
leave +0001 | say

man leave

LEAVE(1)                  BSD General Commands Manual                 LEAVE(1)

NAME
     leave -- remind you when you have to leave

SYNOPSIS
     leave [[+]hhmm]

DESCRIPTION
     The leave utility waits until the specified time, then reminds you that you have to leave.  You are reminded 5 minutes and 1 minute before the actual time, at
     the time, and every minute thereafter.  When you log off, leave exits just before it would have printed the next message.

     The following options are available:

     hhmm    The time of day is in the form hhmm where hh is a time in hours (on a 12 or 24 hour clock), and mm are minutes.  All times are converted to a 12 hour
             clock, and assumed to be in the next 12 hours.

     +       If the time is preceded by `+', the alarm will go off in hours and minutes from the current time.

     If no argument is given, leave prompts with "When do you have to leave?".  A reply of newline causes leave to exit, otherwise the reply is assumed to be a
     time.  This form is suitable for inclusion in a .login or .profile.

     To get rid of leave you should either log off or use `kill -s KILL' giving its process id.

SEE ALSO
     calendar(1)

HISTORY
     The leave command appeared in 3.0BSD.

BSD                             April 28, 1995                             BSD

Thank you!

Advertisement

Answer

It sounds like leave writes one line of output per day. If say is trying to read all of stdin at a go (or otherwise do any kind of a read that is not one-character-at-a-time stopping at the first newline), the buffer will never be full enough for its read to complete in a reasonable time period.

The bash read builtin does these (inefficient) one-character-at-a-time reads, and so is able to get content from a pipeline more appropriately (as long as leave is overriding libc’s default buffering behavior, which switches from line-buffered to fully-buffered when output is not direct to a TTY; but if it doesn’t do this, that’s a bug you should report to your OS vendor).


To run a new copy of say for each line of output from leave:

leave | while IFS= read -r line; do say <<<"$line"; done

To wait until leave has some output, run say exactly once, then exit:

leave | { IFS= read -r line; say <<<"$line"; }

All of this can be put in the background if you choose. For example:

{ leave | { IFS= read -r line; say <<<"$line"; }
} </dev/null >leave-say.log 2>&1 & disown -h "$!"

…will do an equivalent of running the above code in the background with nohup, writing any errors to leave-say.log instead of nohup.out.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement