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!
CodePudding user response:
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
.
CodePudding user response:
What leave
does is fork a background process that gives the leaving messages on STDOUT. When the fork is done, the child process inherits the STDOUT from its parent. When the parent dies, the child keeps the STDIN of the next process in the pipe open. Therefore,
leave 0002 | sed 's/o/OOOO/g'
seems to hang until the child process is killed.
The correct solution would be to background the rest of the pipe:
leave 0002 | sed 's/o/OOOO/g' &
which produces then:
Just OOOOne mOOOOre minute!
Time tOOOO leave!
Or, in your case:
leave 0002 |say &
Killing the child process will stop say
form 'hanging'. The PID of the child is given in the message Alarm set for xx.xx. (pid 3311636)
. You can verify this by killing the child from a different window.
Not that, if you do not leave, the child process will not die, but will keep on nagging from time to time. This means that say
will"
- say "alarm set for pid (output from the parent)
- hang for a while waiting for input
- say "Just one more minute!" (output from the child)
- hang for another minute
- say "Time to leave" (output from the child)
- hang for another minute
- say "Time to leave" (output from the child)
(last two lines repeated until the child is killed)
This is consistent also with the Macos manpage for say
that states:
If the input is a TTY, or if no text is specified, the typed input text will be spoken line by line