0

THE PROBLEM

Crontab does work - but just aside, not in interaction with users. How to make crontab talk to currently logged-in user by automatically perceivable prompt output? For example, a wake-up call, an alarm clock, ringing every minute for user1, without user1 having to do anything for receiving the message, except being present at the computer, with open eyes and ears?

Side note The timing, every minute… - of course, this is just a technical example, in reality a more meaningul and useful timing is chosen.

IMAGINE THIS

Computer = switched off

user1 = dave

dave:


*switching on computer*

*logs into his user account "dave"*

*lands in GUI desktop*

*starts mate-terminal*

Checking who is logged in and what they are doing:

dave@LocalMachine:~$ w
12:46:42 up  4:22,  2 users,  load average: 0.18, 0.44, 0.43
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
dave   tty7     :0               08:24    4:22m  6:57   0.31s mate-session

Creating executable bash script "alarm_clock" in /tmp/

dave@LocalMachine:~$ emacs -nw /tmp/alarm_clock && chmod +x /tmp/alarm_clock
#    Alarm clock displaying current time and ringing.
#
#    Copyleft 🄯    2024
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program. If not, see <https://www.gnu.org/licenses/>.
#
#!/bin/bash

# Passes 2 things to standard output of whoever is executing this script:
## string "current date" - by part $(date)
## acoustic alarm signal - by sequence \a within "" of echo behaving differently by set -e option
echo -e "$(date) \a"

Creating crontab with 1 cronjob for him alone,

with an alarm clock ringing every minute,

expecting a prompt of current time while job execution on screen and an alarm sound,

as if he had given standard input into mate-terminal bash echo -e "$(date) \a"

dave@LocalMachine:~$ crontab -e
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command

# Variables
## Otherwise will spill out "TERM environment variable not set." as 2
### It is set - but just for user "dave", not crontab itself
#### … until now:
TERM=xterm

# Test
## sh-file "alarm_clock"
### executable for dave, dave (group) & others
### containing shebang bash
### containing NEWLINE as last character of the document
### working when being executed by dave
### redirecting standard output, split up into results.log for successful parts, and errors.log for errors
* * * * * /tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log

THE OUTPUT

dave@LocalMachine:~$ tail -f /var/log/syslog
Jan  2 13:52:01 LocalMachine CRON[9786]: (dave) CMD (/tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log)
Jan  2 13:53:01 LocalMachine CRON[9807]: (dave) CMD (/tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log)
Jan  2 13:53:28 LocalMachine kernel: [19747.884955] [UFW BLOCK] IN=enp0s25 OUT= MAC=01:00:5e:00:00:01:38:43:7d:3a:bd:51:08:00 SRC=192.168.0.1 DST=224.0.0.1 LEN=36 TOS=0x00 PREC=0xC0 TTL=1 ID=26121 PROTO=2 
Jan  2 13:54:01 LocalMachine CRON[9813]: (dave) CMD (/tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log)
Jan  2 13:55:01 LocalMachine CRON[9818]: (dave) CMD (/tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log)
Jan  2 13:55:33 LocalMachine kernel: [19872.884504] [UFW BLOCK] IN=enp0s25 OUT= MAC=01:00:5e:00:00:01:38:43:7d:3a:bd:51:08:00 SRC=192.168.0.1 DST=224.0.0.1 LEN=36 TOS=0x00 PREC=0xC0 TTL=1 ID=26123 PROTO=2 
Jan  2 13:55:46 LocalMachine com.canonical.indicator.sound-gtk2[1651]: ** (process:2001): WARNING **: music-player-bridge.vala:262: Could not create a desktopappinfo instance from app: firefox.desktop
Jan  2 13:55:46 LocalMachine com.canonical.indicator.sound-gtk2[1651]: ** (process:2001): WARNING **: music-player-bridge.vala:181: Could not create app_info for path firefox
Jan  2 13:55:46 LocalMachine com.canonical.indicator.sound-gtk2[1651]:  Getting out of here
Jan  2 13:56:01 LocalMachine CRON[9857]: (dave) CMD (/tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log)
dave@LocalMachine:~$ emacs -nw /tmp/my_command_results.log 
-e Tue  2 Jan 14:01:01 CET 2024 ^G

side note

That ^G, that's unexpected. Can't explain to me where that comes from, judging the script just contained a simple echo -e "$(date) \a" and passing this manually into mate-terminal bash gives standard output Tue 2 Jan 14:06:57 CET 2024 + a popping sound as alarm signal.

Now it becomes even weirder

Having realized I've took a look into that file with en editor, instead simply invocing cat, I've repeated with process with the latter, triggering strange output

dave@LocalMachine:~$ cat /tmp/my_command_results.log
-e Tue  2 Jan 14:11:01 CET 2024

PLUS a popping sound as alarm signal, as if I hadn't invoced cat but an echo like for example echo -e "$(date) \a" ! How weird is that?!

my_command_errors.log is empty either way:

dave@LocalMachine:~$ cat /tmp/my_command_errors.log
dave@LocalMachine:~$ emacs -nw /tmp/my_command_errors.log
dave@LocalMachine:~$ 

TEST PHASE 2: IMPLEMENTATION

Now with that all out of the way, dave is switching the crontab active, by creating a copy of the line, removing the part redirecting the output into files, away from standard output, and commenting the copy source out

Before

dave@LocalMachine:~$ crontab -e
* * * * * /tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log

After

dave@LocalMachine:~$ crontab -e
# * * * * * /tmp/alarm_clock > /tmp/my_command_results.log 2>/tmp/my_command_errors.log
* * * * * /tmp/alarm_clock

THE OUTPUT AFTER ACTIVATION

dave@LocalMachine:~$ tail -f /var/log/syslog
Jan  2 14:28:02 LocalMachine CRON[10175]: (dave) CMD (/tmp/alarm_clock)
Jan  2 14:28:02 LocalMachine CRON[10174]: (CRON) info (No MTA installed, discarding output)
Jan  2 14:28:53 LocalMachine kernel: [21872.877424] [UFW BLOCK] IN=enp0s25 OUT= MAC=01:00:5e:00:00:01:38:43:7d:3a:bd:51:08:00 SRC=192.168.0.1 DST=224.0.0.1 LEN=36 TOS=0x00 PREC=0xC0 TTL=1 ID=26155 PROTO=2 
Jan  2 14:29:01 LocalMachine CRON[10182]: (dave) CMD (/tmp/alarm_clock)
Jan  2 14:29:01 LocalMachine CRON[10181]: (CRON) info (No MTA installed, discarding output)
Jan  2 14:30:01 LocalMachine CRON[10191]: (dave) CMD (/tmp/alarm_clock)
Jan  2 14:30:01 LocalMachine CRON[10190]: (CRON) info (No MTA installed, discarding output)
Jan  2 14:30:58 LocalMachine kernel: [21997.876838] [UFW BLOCK] IN=enp0s25 OUT= MAC=01:00:5e:00:00:01:38:43:7d:3a:bd:51:08:00 SRC=192.168.0.1 DST=224.0.0.1 LEN=36 TOS=0x00 PREC=0xC0 TTL=1 ID=26157 PROTO=2 
Jan  2 14:31:01 LocalMachine CRON[10195]: (dave) CMD (/tmp/alarm_clock)
Jan  2 14:31:01 LocalMachine CRON[10194]: (CRON) info (No MTA installed, discarding output)
Jan  2 14:32:01 LocalMachine CRON[10202]: (dave) CMD (/tmp/alarm_clock)
Jan  2 14:32:01 LocalMachine CRON[10201]: (CRON) info (No MTA installed, discarding output)

Aside of missing redirection output no changes, still everything is quiet in the room with the computer, although according to syslog the cronjob is executed every minute. And a prompt of the current date as if the user manually types in echo "$(date)" and then RET, doesn't appear either. Silence on all channels. How can that be? And more important: What to change for automatically receiving a popping sound as alarm signal every minute and display of current time of that event on the screen?

5
  • From man ascii: 007 7 07 BEL '\a' (bell). The terminal renders BEL as^G (BEL is octal 007, G is octal 107). Commented Jan 2 at 14:13
  • cron explicitly runs jobs in the background. stdin is from /dev/null, stdout and stderr are saved and (by default) emailed to the user. The job does not inherit a terminal (there may be no logged-in user at all, and how would you set window focus anyway). Sound is less restrictive (I can * * * * * spd-say -r -60 -p -50 -i -40 Doh). Commented Jan 2 at 14:24
  • cron is not meant to interact with users, and your usecase doesn't sound like a job for cron, Commented Jan 2 at 14:25
  • Cron job not running on Stack Overflow has an answer by me with some ideas around this question. In brief, "not even wrong".
    – tripleee
    Commented Jan 2 at 14:35
  • Note: the bash script doesn't start with #!/bin/bash in the first line. I doubt the kernel will execute it (but it can still be executed as a parameter to bash, but not in your case).
    – A.B
    Commented Jan 2 at 16:35

1 Answer 1

0

^G character

First of all, when you do echo '\a', the bell is being sound by the terminal that received it. It's just an ASCII code with the octal value of 007. It is also equivalent to press CTRL+G (`^G).

from man 7 ascii:

       Oct   Dec   Hex   Char                        Oct   Dec   Hex   Char
       ────────────────────────────────────────────────────────────────────────
       000   0     00    NUL '\0' (null character)   100   64    40    @
       001   1     01    SOH (start of heading)      101   65    41    A
       002   2     02    STX (start of text)         102   66    42    B
       003   3     03    ETX (end of text)           103   67    43    C
       004   4     04    EOT (end of transmission)   104   68    44    D
       005   5     05    ENQ (enquiry)               105   69    45    E
       006   6     06    ACK (acknowledge)           106   70    46    F
       007   7     07    BEL '\a' (bell)             107   71    47    G

Usually this character is not visible:

$ echo -e "$(date) \a"
Tue Jan  2 18:31:38 IST 2024 

But you can see it by adding the -v / --show-nonprinting flag to cat command (use ^ and M- notation, except for LFD and TAB):

$ echo -e "$(date) \a" |cat -v
Tue Jan  2 18:31:40 IST 2024 ^G

So when you just write this ascii code to a file - and not to a terminal - you will just write the value of octal 007 char to the file. Only when you write it to a terminal you'll hear a "beep" sound.

-e not used by echo

Another problem you have with your script, is that it doesn't start with the Shebang (#!), but with a comment. In that case your #!/bin/bash is ignored, and the script will probably be executed by /bin/sh. See man 5 crontab:

Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh [...]

/bin/sh on your machine might be some POSIX adherent shell in which the echo builtin that doesn't support the -e flag (which is not defined by POSIX and is usually just an extension). In that case, it will use the -e as part of the string you want to echo. If you want to make sure you use bash, move the comments to after the Shebang.

Also, for this case it's better to use printf, for instance: printf "$(date) \a\n"

Sending the message to an actual terminal

As we said, when you write it to a file, you don't interact with any terminal, you just write some character to a file. If you want to send the message to a terminal, it's better to use write(1), which will send the message to the last active tty of a user. For instance:

printf "$(date)\a\n" | write <USER>

At which case the user will receive a message on his active shell such as:

Message from root@locahost on pts/0 at 19:00 ...
Tue Jan  2 19:00:49 IST 2024 
EOF

Or if you want to send the message to ALL users and terminal, use wall(1).

printf "$(date)\a\n" | wall

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .