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
exitAnd 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/zzyxyTo 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.
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"
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!"
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.
1This 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.shand 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.0The 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.
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.shAdd 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
30789I 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-notifySee 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 stuffIt 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/nullcrontab:
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>&1I 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."
endI 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=:1Solved 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.