I made a script that should notify me when there's a new chapter of manga that I'm reading. I used the command notify-send to do this. The program works when I am trying to run it in terminal. The notification is showing. However, when I placed this in my crontab, the notification doesn't show. I'm pretty sure that the program is running since I made it to create a file for me. The file was created, but the notification didn't show.

Here's my script

#!/bin/bash #One Piece Manga reminder #I created a file named .newop that contains the latest chapter. let new=$(cat ~/.newop) wget --read-timeout=30 -t20 -O .opreminder.txt if (( $(cat .opreminder.txt | grep "One Piece $new" | wc -l) >=1 )) then (( new+=1 )) echo $new echo $new > ~/.newop notify-send "A new chapter of One Piece was released." else notify-send "No new chapter for One Piece." notify-send "The latest chapter is still $new." fi exit 

And here's what I wrote in my crontab

0,15,30,45 12-23 * * 3 /home/jchester/bin/opreminder.sh 
5

20 Answers

Things seem to be different on 13.04, at least in Gnome Shell.

First, this is what env prints when run from user zzyxy's (not root's) cron job:

HOME=/home/zzyxy LOGNAME=zzyxy PATH=/usr/bin:/bin XDG_RUNTIME_DIR=/run/user/zzyxy LANG=en_US.UTF-8 SHELL=/bin/sh PWD=/home/zzyxy 

To get notify-send to work, it seems to be necessary to set the DBUS_SESSION_BUS_ADDRESS environment variable, as per DahitiF's comment on ubuntuforums.org. Just prepend the following to your actual job description:

eval "export $(egrep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u $LOGNAME gnome-session)/environ)"; 

It doesn't seem to be necessary to set DISPLAY.

7

Command notify-send would not show the message on your screen when started by cron. Just add target display at the top of your script, for example:

export DISPLAY=:0 
4

Commands need to reference their location. So notify-send needs to be /usr/bin/notify-send

All commands need to have their full path.

Use the whereis notify-send command to see where your commands "live"

8

I use i3 on Ubuntu 18.04 and 20.04. My way to solve this is:

* * * * * XDG_RUNTIME_DIR=/run/user/$(id -u) notify-send Hey "this is dog!"

3

For Ubuntu 14.04 at least, klrmr's response above is the correct answer. It does not appear to be necessary to set DISPLAY or articulate full paths for notify-send or anything otherwise normally in $PATH.

Below is a cron script I'm using to shutdown a virtual machine when a laptop's battery state becomes too low. The line setting DBUS_SESSION_BUS_ADDRESS in klrmr's response above is the modification that finally got the warnings working correctly.

#!/bin/bash # if virtual machine is running, monitor power consumption if pgrep -x vmware-vmx; then bat_path="/sys/class/power_supply/BAT0/" if [ -e "$bat_path" ]; then bat_status=$(cat $bat_path/status) if [ "$bat_status" == "Discharging" ]; then bat_current=$(cat $bat_path/capacity) # halt vm if critical; notify if low if [ "$bat_current" -lt 10 ]; then /path/to/vm/shutdown/script echo "$( date +%Y.%m.%d_%T )" >> "/home/user/Desktop/VM Halt Low Battery" elif [ "$bat_current" -lt 15 ]; then eval "export $(egrep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u $LOGNAME gnome-session)/environ)"; notify-send -i "/usr/share/icons/ubuntu-mono-light/status/24/battery-caution.svg" "Virtual machine will halt when battery falls below 10% charge." fi fi fi fi exit 0 
2

In my case with ubuntu 16.04 any explicit path was required, I solve the problem just adding

DISPLAY=:0

at firsts lines of the crontab, before call notify-send.

1

This took forever to make work on ubuntu 15.10, Had to add a source to get the users normal env vars. my display was :1 for some reason as well. Using the gnome-session first results pid for DBUS_SESSION_BUS_ADDRESS lookup.

# Crontab is * 21 * * * /bin/sh /home/tristik/cron.sh 
#!/bin/sh # cron.sh # Notifies the user of date and time source /home/tristik/.bashrc pid=$(pgrep -u tristik gnome-session | head -n 1) dbus=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$pid/environ | sed 's/DBUS_SESSION_BUS_ADDRESS=//' ) export DBUS_SESSION_BUS_ADDRESS=$dbus export HOME=/home/tristik export DISPLAY=:1 /usr/bin/notify-send 'title' "$(/bin/date)" 
1

First culprit is your crontab file, you also need to mention the user name with which the script has to be executed, better keep it as root

0,15,30,45 12-23 * * 3 root /home/jchester/bin/opreminder.sh 

and then you should use the user_name of the GUI user inside the script and prepend it to notify-send with "sudo or su" to execute the command as a user who owns the GUI

example :

su gnome_user_name -c 'notify-send "summary" "body"' 

or

sudo -u gnome_user_name notify-send "summary" "body" 

where gnome_user_name is the username of the user who started the GUI session it is you who logged in, and if you want to make it a dynamic pick, you can get it from

GNOME_USER=`ps -eo uname,cmd | grep gnome-session| head -1 | cut -d' ' -f1 ` 

example :

su $GNOME_USER -c 'notify-send "summary" "body"' 

or

sudo -u $GNOME_USER notify-send "summary" "body" 
3

The way the binary retrieves the dbus address seems to have changed lately. On Ubuntu 15.04 (Vivid Vervet) with "notify-send 0.7.6", the following two variables are needed:

export HOME=/home/$notify_user export DISPLAY=:0.0 

The statement by 'krlmlr' evaluates fine and sets the correct address, but the dialog won't pop up from a cron job.

Use printenv for print environment variables from your normal terminal. And then paste all environment variables in starting of crontab file.

1

If your script in crontab is running as root, the answers above will probably not work. Try this function, which works fine for me in 16.04 :

notify_all() { local title=$1 local msg=$2 who | awk '{print $1, $NF}' | tr -d "()" | while read u d; do id=$(id -u $u) . /run/user/$id/dbus-session export DBUS_SESSION_BUS_ADDRESS export DISPLAY=$d su $u -c "/usr/bin/notify-send '$title' '$msg'" done } 

( Source: )

Better to rely on dbus-session process, it should be running for all systems where DBUS_SESSION_BUS_ADDRESS is present.

Create a script:

#!/bin/bash # notify.sh environs=`pidof dbus-daemon | tr ' ' '\n' | awk '{printf "/proc/%s/environ ", $1}'` export DBUS_SESSION_BUS_ADDRESS=`cat $environs 2>/dev/null | tr '\0' '\n' | grep DBUS_SESSION_BUS_ADDRESS | cut -d '=' -f2-` export DISPLAY=:0 notify-send "It works!" 

Make it executable:

$ chmod +x ~/notify.sh 

Add it to crontab:

* * * * * $HOME/notify.sh 
1

I have just got this to work with the cinnamon desktop on Ubuntu 15.10, using the following recipe:

if [ ! -v DBUS_SESSION_BUS_ADDRESS ]; then pid=$(pgrep -u $LOGNAME cinnamon-sessio) eval "export $(\grep -z DBUS_SESSION_BUS_ADDRESS /proc/$pid/environ)" fi notify-send "$RESUME" "$INFO" 

The trick was to realize that 'cinnamon-session' is too long for pgrep to find:

$ pgrep -u $LOGNAME cinnamon-session $ pgrep -u $LOGNAME cinnamon 30789 30917 30965 30981 31039 31335 $ ps -a | \grep cinnamon 30789 tty2 00:00:00 cinnamon-sessio 30917 tty2 00:00:02 cinnamon-settin 30965 tty2 00:00:00 cinnamon-launch 30981 tty2 00:04:15 cinnamon 31039 tty2 00:00:00 cinnamon-killer 31335 tty2 00:00:00 cinnamon-screen $ ps a | \grep cinnamon 4263 pts/1 S+ 0:00 grep cinnamon 30779 tty2 Ssl+ 0:00 /usr/lib/gdm/gdm-x-session --run-script cinnamon-session-cinnamon 30789 tty2 Sl+ 0:00 cinnamon-session --session cinnamon 30917 tty2 Sl+ 0:02 /usr/lib/x86_64-linux-gnu/cinnamon-settings-daemon/cinnamon-settings-daemon 30965 tty2 Sl+ 0:00 /usr/bin/python2 /usr/bin/cinnamon-launcher 30970 tty2 Sl+ 0:00 /usr/lib/x86_64-linux-gnu/cinnamon-settings-daemon/csd-printer 30981 tty2 Sl+ 4:16 cinnamon --replace 31039 tty2 Sl+ 0:00 /usr/bin/python2 /usr/bin/cinnamon-killer-daemon 31335 tty2 Sl+ 0:00 cinnamon-screensaver $ pgrep -u $LOGNAME cinnamon-sessio 30789 

I also had to use \grep because my grep is aliased to

$ alias grep alias grep='grep -n --color=always' 

Issue caused by calling python3 in crontab with UTF-8 locale.

TL;DR: prefix call in crontab w/ locale as in:

*/5 * * * * LC_ALL=en_US.utf-8 LANG=en_US.utf-8 ~/.local/bin/watson-notify 

See also click and python3:

Traceback (most recent call last): File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main "__main__", mod_spec) File "/usr/lib/python3.6/runpy.py", line 85, in _run_code exec(code, run_globals) File "/usr/lib/python3/dist-packages/watson/__main__.py", line 6, in <module> cli.cli() File "/usr/lib/python3/dist-packages/click/core.py", line 759, in __call__ return self.main(*args, **kwargs) File "/usr/lib/python3/dist-packages/click/core.py", line 693, in main _verify_python3_env() File "/usr/lib/python3/dist-packages/click/_unicodefun.py", line 123, in _verify_python3_env 'for mitigation steps.' + extra) RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Consult for mitigation steps. This system supports the C.UTF-8 locale which is recommended. You might be able to resolve your issue by exporting the following environment variables: export LC_ALL=C.UTF-8 export LANG=C.UTF-8 

For all crontab script that use libnotify, I use this:

notify_user() { local user=$(whoami) notify-send -u normal -t 4000 "System Backup" "Starting backup" } notify_user # and do other stuff 

It works even if I use cron in root mode.

All you need is the X_user and X_userid. Replace both in the command bellow.

Solution with systemd

/etc/systemd/system/opreminder.service #Service file

[Unit] Descrption=some service to run [Service] User=[X_user] ExecStart=/home/jchester/bin/opreminder.sh 

/etc/systemd/system/opreminder.timer #timer file

[Unit] Description=Some desc [Timer] OnCalendar=0,15,30,45 12-23 * * 3 [Install] WantedBy=list.timer.target 

/home/jchester/bin/opreminder.sh #The script

#!/usr/bin/env bash sudo -u [X_user] DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/[X_userid]/bus notify-send 'Hello world!' 'This is an example notification.' 

No need to use sudo -u if the service file is already set with the intended user

Source:

This makes it work on 19.10:

eval "export $(pgrep -u $LOGNAME gnome-session | head -n 1 | xargs -I{} cat /proc/{}/environ | egrep -z DBUS_SESSION_BUS_ADDRESS)"; 

In the case below, I was calling notify-send from a python script that monitors memory of processes as I've been having issues with XOrg memory growth.

The example below should work and does not output the warning: command substitution: ignored null byte in input warning.

myscript_cron.sh:

#!/bin/bash echo $0 called: `date` export USER=`whoami` export HOME=/home/$USER export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u ${USER} gnome-session | head -n 1)/environ | tr '\0' '\n'| sed 's/DBUS_SESSION_BUS_ADDRESS=//') cd <path_to_my_script> /usr/bin/python3.7 ./<my_script>.py 2>&1 1>/dev/null 

crontab:

Note: Ran as crontab -e in my user account and not sudo

* * * * * <path_to_my_script>/myscript_cron.sh >> <path_to_my_script>/cron.log 2>&1 

I like to monitor output of cron calls to a log file so I can debug the shell script but I don't want it to be polluted with the stdout of the python script. I output the python script to a separate log.

For whoever is using the fish shell, like me, here is my script which essentially does the same as the bash script of @denis.peplin. I use it to send a warning when the battery charge is low.

#!/bin/fish # set environment variables pidof dbus-daemon | tr ' ' '\n' | while read -l pid set environs $environs /proc/$pid/environ end set -x DBUS_SESSION_BUS_ADDRESS (cat $environs 2>/dev/null | tr '\0' '\n' | grep DBUS_SESSION_BUS_ADDRESS> set -x DISPLAY :0 # perform actual job set capacity (cat /sys/class/power_supply/BAT0/capacity) if test $capacity -le $argv[1] /usr/local/bin/dunstify -u critical -t 10000 "Battery low!" "Plug in asap." end 

I had to avoid awk, because its return type is a string containing two paths that are separated by a space. A fish variable does not extract the two paths, but stores the whole string, causing conflicts when using cat $environs. Using a while loop that appends the paths to the variable achieves the intended.

The solution with setting DISPLAY=:0.0 worked for me for years. In 20.04, it suddenly stopped working. It turned out that now the display coordinates changed, it's now :1. So

export DISPLAY=:1 

Solved the problem.

The styling of the popup is ugly, but it's a different story.

Update

What I wrote above was about the case of upgrading from 16.04 via 18.04 to 20.04.

But when I reinstalled Ubuntu 20.04 from scratch (still keeping /home/$USER), it stopped working (probably, notify-send is now implemented by a different program).

So, now the DISPLAY variable is not relevant, but here is what I need to define: DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus.

So a line in crontab could look like this:

14 * * * * DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus notify-send 'Hello!' 

The pop-up has the same styling as when running notify-send from a terminal emulator.