I have been working in a script to change audio output when you (dis)connect am HDMI screen/TV. Basically it uses UDEV to check any change in the connection and acts accordingly.

Everything works fine except one thing: I can't use notify-send to warn about the change. I use the following code (output is just a wrapper to subprocess.check_output):

output("sudo -u {0} notify-send \"{1}\" \"{2}\"".format(user, title, message))

but I don get any notification at all.

What is really odd is that if I run hdmi_sound_toggle (as a normal user or with sudo) everything works just fine!

So what could be the problem here? There is any better way to present a notification than with notify-send?

  • Have you tried sticking a notify-send in the script that just says "hi" or something simple? Just to see if it can be called properly from the script. Perhaps the formatting is off. – Steelsouls Sep 21 '13 at 16:56

The notify-send has trouble putting notifications on a user's screen when called from a script run by root or another user.

You should use:

output("export DISPLAY=:0; sudo -u {0} notify-send \"{1}\" \"{2}\"".format(user, title, message))

So, use:

export DISPLAY=:0

Normally a user is on display :0, but to be sure, you can find which display a user is on using who command as follow:

who | grep -m1 ^username.*\( | awk '{print $5}' | sed 's/[(|)]//g'

This worked for me in this script.

See also: Can I launch a graphical program on another user's desktop as root?

  • who | grep -m1 ^username.*\( | awk '{print $5}' | sed 's/[(|)]//g' works well but it's a little bit unreliable. Because it doesn't take into account that user name is followed by whitespaces in who's output. So it will be better to use who | grep -m1 ^username[[:blank:]].*\( | awk '{print $5}' | sed 's/[(|)]//g'. – flaz14 Aug 10 '16 at 10:35

I was researching a consistent way to notify all X users (as root) and my research (looking at a bunch of places on the net) indicated that although it was possible to use notify-send in this way, it required scripting to find DBUS session info and/or DISPLAY's in use and/or Xauthority file names and locations, and even then sometimes people still couldn't get it to work. In summary, trying to bend notify-send to your will to enable broadcasting to X users will prove to be unreliable (particularly if you upgrade or change distro at some point in the future).

However... on my system the wall command worked like a charm and is incredibly simple to use. The only downside is that it also broadcasts the message to all text terminals/consoles as well. If you don't mind that, then wall might be a better and more reliable option. Note: the user invoking wall must be root.


Radu's answer does not seem to work for me on 16.04. Rather than try to fight the DISPLAY problem which seems to change with different releases, I go about this in a slightly different manner. I create a single script that is called in two ways. A udev rule calls the script, sending some kind of output to a named pipe. Upon login, the script is also started in a service mode. Here it listens to the pipe and runs the desired GUI command.

As an example, I like to receive notifications of USB storage devices as they are attached, and in particular, I like to know what the path of the device is and its serial number (that may seem strange but I have good reasons). Here's the script I use to accomplish this.

# script name: usb-notify.sh
# This script needs to be called at logon with "service" parameter
# This script needs to be called by udev on USB insertion with no parameter

if [ "$1" == "service" ]; then
    # create the named pipe that will help us trigger events from udev
    rm "$pipe"
    mkfifo "$pipe"

    # This outer while loop ensures that we can process multiple triggers
    while true
        # This inner while loop reads individual trigger events
        while read info
            # run some GUI command. Here, I'm running notify-send
            notify-send "USB Inserted" "$(echo -e $info)"
        done < "$pipe"
    # Here's where udev triggers our event. If you don't need to pass any arguments
    # you  could just as easily pass an empty line as a trigger for the service.
    [ -e "$pipe" ] && echo "Device: $DEVNAME\\\nSN: $ID_SERIAL_SHORT" > "$pipe"

This script then needs to be called in two ways. First, a udev rule needs to be created to call this script. This can be done with the following command (obviously you should change this to watch whatever device should be triggering your script):

echo 'ACTION=="add",KERNEL=="sd?",RUN+="[path-to-script]/usb-notify.sh"' | sudo tee -a /etc/udev/rules.d/80-USB.rules

Second, you need to call this script with the "service" parameter on login. So, open the "Startup Applications" program and add an entry that runs "[path-to-your-script]/usb-notify.sh service"

This script can be modified to run pretty much any GUI application on udev events. If you don't want other users to be able to watch your pipe, you should probably set restrictive permissions on it.


Do you need to call it with sudo? That might solve the problem.

Alternatively, I found I needed DISPLAY=:0 and XAUTHORITY=/home/<user_name>/.Xauthority set for it to work when running as root (from a systemd service in this case - set with Environment=...).


You can use pynotify instead of a shell command. It's quite simple and is installed by default in Ubuntu. A quick sample:

import pynotify

n = pynotify.Notification("Name", "This is a notification")
  • Did you try running this from root? I get Error: The connection is closed on n.show() – Gus Feb 10 '15 at 20:34
  • No, not really. – Javier Rivera Feb 11 '15 at 7:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.