67

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 http://www.mangareader.net/103/one-piece.html

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
6
  • Just a reminder, all commands in crontab need to have their path in front of them as they run as root. Attaching script and line in crontab would help otherwise we are just guessing at your problem
    – Meer Borg
    May 22, 2013 at 11:44
  • Yeah, sorry. I just did.
    – user158335
    May 22, 2013 at 12:01
  • This is a bad idea. Notifications are a "GUI" thing, cron is a "console" thing. There is no gaurentee that lib-notify will be able to find a way to display the message. Instead you should consider sending data to stdout and let cron's messaging take care of sending the info. Normally an email is sent.
    – coteyr
    May 22, 2013 at 12:53
  • 2
    In some cases setting the DISPLAY variable up may help as well, e.g.: export DISPLAY=:0. May 22, 2013 at 14:50
  • 3
    For 16.04, this one worked for me */1 * * * * eval "export $(egrep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u $LOGNAME gnome-session)/environ)";/usr/bin/notify-send -i appointment -c "im" "Keep Working"
    – Midhun KM
    Jul 8, 2018 at 5:57

21 Answers 21

46

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
  • 5
    Thanks, this is what finally worked for me. On Xubuntu, you have to change gnome-session to xfce4-session.
    – shrx
    Feb 19, 2015 at 10:05
  • This is the only answer to work for 14.04, along with the obvious hint of the accepted one.
    – Wtower
    Feb 28, 2015 at 11:34
  • 1
    I had no gnome-session and used gnome-shell instead (be careful there is also a gnome-shell-calendar-server so pgrep will get 2 pids). I also needed DISPLAY=:0 because I'm using 2 phisical screens and it was not defined. Thanks!
    – soyuka
    May 27, 2015 at 15:05
  • If you're using Openbox (like on CB++) swap in openbox for gnome-session. Oct 3, 2016 at 19:06
  • 4
    Use echo $DESKTOP_SESSION to findout your session name, mine was plasma because I used Kubuntu.
    – Liso
    May 20, 2020 at 6:31
31

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
  • This is what I had to do in 14.10 too. Otherwise I would get this error gdk_mir_display_open Failed to connect to Mir: Failed to connect to server socket: No such file or directory Option parsing failed: Cannot open display:
    – Joelmob
    Oct 29, 2014 at 23:43
  • 3
    This. And use echo $DISPLAY in a terminal to make sure your display really is :0 (it usually is, but not always).
    – Mark
    Feb 7, 2016 at 16:16
  • Only this worked for me, I am using Linux Mint Apr 6, 2016 at 8:32
  • I'm on Ubuntu 18.04 and this did not work DBUS_SESSION_BUS_ADDRESS is the issue there. Mar 8, 2020 at 17:04
23

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
  • 2
    Does that include cat, wget, if, let, grep, echo, etc?
    – user158335
    May 22, 2013 at 12:10
  • 9
    At least on my system, notify-send is on the PATH even for a cron job. See my answer below.
    – krlmlr
    Sep 17, 2013 at 9:11
  • 2
    It isn't the solution for Ubuntu 17.04. See askubuntu.com/a/472769/413683 and askubuntu.com/a/834479/413683 instead. May 7, 2017 at 11:39
  • 4
    This is not the problem. The problem is that cron scripts do not run under the user session, and have no concept of the environment of the user's login session. As notify-send requires connecting to a dbus session bus to send the notification, it will not matter what path the binary is called from, when it doesn't connect to the correct session bus.
    – dobey
    Oct 26, 2017 at 21:30
  • 6
    This is NOT the answer. Sure, if executable cannot be located,it won't run, BUT: 1. notify-send is on the PATH so it will be located 2. even if it wasn't on the PATH, and you specify full path it still will not work, because actually DBUS_SESSION_BUS_ADDRESS has to be set for notify-send. And the correct answer is from kmir.
    – Kris Jace
    Jan 24, 2019 at 1:53
17

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!"

4
  • 5
    That worked for me, Added export line on top of my script file. export XDG_RUNTIME_DIR=/run/user/$(id -u)
    – Prabah
    Jul 19, 2020 at 10:37
  • 3
    That worked for me on Ubuntu 20.04.2 LTS as well.
    – dvlcube
    Feb 12, 2021 at 20:20
  • 1
    Thank you. export XDG_RUNTIME_DIR=/run/user/$(id -u) was exactly what i needed. Oct 5, 2021 at 9:25
  • Thanks that works fine. Mar 13, 2023 at 5:57
7

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
  • This was the solution that worked perfectly for me as well, I just added the "eval..." line to my script that I run from crontab - now it works perfectly
    – Mtl Dev
    May 16, 2019 at 19:31
  • I also needed to add this to the top of my script: export XDG_RUNTIME_DIR=/run/user/$(id -u) Aug 23, 2021 at 18:25
5

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 is the only thing necessary to get it to work on 16.04. Apr 3, 2018 at 21:15
2

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
  • used this to solve a screensaver problem
    – jdl
    Dec 23, 2020 at 17:53
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
  • 1
    I think that when your username is longer than X characters, your usernma is truncated: For example, my username is oniltonmaciel, but $GNOME_USER would show onilton+ (doesn't work) Apr 25, 2015 at 21:03
  • fixed it with better command
    – Sun
    Apr 27, 2015 at 9:01
  • Better edit the sudoers and avoid root shell.
    – Emmanuel
    Jan 10, 2021 at 1:13
1

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.

1

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.

1
  • I tried quite a few solutions. Only this one worked for me on Ubuntu 22.04.2. Thank you.
    – Rafal
    Jul 29, 2023 at 15:12
1

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

1
  • 1
    Please don't submit images of text. We can't work with it easily, can't copy it accurately, and some users can't read it at all. Instead copy-paste the text into the body of your question and render the text in monospace font so it is easier to work with. You can use the editing tools above the text box when you edit your post or you can also use Markdown language.
    – Nmath
    Nov 21, 2020 at 22:22
0

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: https://unix.stackexchange.com/a/344377/7286 )

0

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
  • 1
    FYI - Setting DISPLAY was not needed for me. Mar 8, 2020 at 17:06
0

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'
0

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 http://click.pocoo.org/python3/ 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
0

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.

0

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: https://wiki.archlinux.org/index.php/Desktop_notifications#Usage_in_programming

0

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)";
0

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.

0

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.

0

In my case, the issue was with using root user with notify-send. I realized this when I saw that sudo notify-send didn't work on the terminal but just notify-send (i.e. with current user) did. So instead of editing the usual /etc/crontab which is used by root, I had a custom cron script created for myusername. Please check my whole answer here.

You must log in to answer this question.

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