在所有正在运行的X显示屏上显示通知


16

我想使用命令行在每个正在运行的X显示器上显示一条通知。(和正在运行的控制台)

就像是:

notify-send-all 'Warning' 'Nuclear launch in 5 minutes, please evacuate'

有程序可以做到这一点吗?如果没有,可以使用bash来实现吗?


1
对于多年后来到这里的人们,此答案中有一个简单的notify_all函数,该函数可在Ubuntu 16.04中使用,并且可以在由root启动的脚本中使用。
mivk

Answers:


16

您可以使用命令墙向所有控制台发送消息。

为了在X下发送通知,有notify-send,它在当前显示器上向当前用户发送通知。(根据您的问题,我想您已经知道这一点。)您可以使用一些bash脚本在此基础上建立。基本上,您必须找出哪些用户在哪个X显示器上。获得此信息后,您可以像这样使用notify-send:

DISPLAY=:0 sudo -u fschmitt notify-send "Message"

其中fschmitt是显示0处的用户。您可以解析“ who”命令的输出以查找所有用户及其显示。输出看起来像这样

[edinburgh:~]$ who
markmerk3 tty7         2010-09-23 10:59 (:0)
markmerk3 pts/1        2010-09-30 13:30 (:0.0)
fschmitt pts/2        2010-10-08 11:44 (ip-77-25-137-234.web.vodafone.de)
markmerk3 pts/0        2010-09-29 18:51 (:0.0)
seamonkey pts/6        2010-09-27 15:50 (:1.0)
markmerk3 pts/5        2010-09-27 14:04 (:0.0)
seamonkey tty8         2010-09-27 15:49 (:1)
markmerk3 pts/13       2010-09-28 17:23 (:0.0)
markmerk3 pts/3        2010-10-05 10:40 (:0.0)

您会看到有两个用户正在运行X会话,显示0处为markmerk3,显示1处为seamonkey。我认为您需要对tty [0-9] *使用grep,然后确保在行的末尾有(:[0 -9。] *)摆脱控制台登录,并从括号之间的字符串中提取显示ID。


2
该命令who告诉您登录的用户以及该登录在哪个X显示屏上。您可能只需要对其进行某种程度的过滤。
tante

1
尽管在Shell脚本中使用循环可能更好,但是您总是可以这样做who | awk '/\(:[0-9]+\)/ {gsub("[:|(|)]","");print "DISPLAY=:"$5 " sudo -u " $1 " notify-send \"Message\""}' | bash。此外,您可能希望看到unix.stackexchange.com/questions/1596/...
史蒂芬d

8

抱歉,该线程有点旧,但是我希望我仍然可以为该主题添加一些有用的东西。(同样,Josef Kufner编写了一个不错的脚本,对于我来说,这有点太长了,它使用了PHP)

我还需要原始问题中所述的工具(向所有活动的X用户发送消息)。根据此处的答案,我编写了这个仅bash的小脚本,该脚本搜索活动的X用户(使用“ who”),然后为每个活动的用户运行notify-send。

最好的是:您可以像使用“通知发送”一样使用我的脚本,并带有所有参数!;-)

通知所有发送:

#!/bin/bash
PATH=/usr/bin:/bin

XUSERS=($(who|grep -E "\(:[0-9](\.[0-9])*\)"|awk '{print $1$5}'|sort -u))
for XUSER in $XUSERS; do
    NAME=(${XUSER/(/ })
    DISPLAY=${NAME[1]/)/}
    DBUS_ADDRESS=unix:path=/run/user/$(id -u ${NAME[0]})/bus
    sudo -u ${NAME[0]} DISPLAY=${DISPLAY} \
                       DBUS_SESSION_BUS_ADDRESS=${DBUS_ADDRESS} \
                       PATH=${PATH} \
                       notify-send "$@"
done

将以上代码复制到名为“ notify-send-all”的文件中,使其可执行,然后将其复制到/ usr / local / bin/ usr / bin(根据需要)。然后以root身份在控制台会话中运行它,如下所示:

notify-send-all -t 10000 "Warning" "The hovercraft is full of eels!"

我已经在不同的机器上使用了几个月,到目前为止,还没有任何问题,并且已经在MATE和Cinnamon台式机上进行了测试。还可以在cron和anacron中成功运行它。

我是在/ ArchLinux下编写此脚本的,所以请告诉我在其他Linux发行版或台式机上是否遇到问题。


|egrep?? egrep是命令吗?
Sw0ut

@ Sw0ut,egrep确实是一个命令。但是在grep(1)的手册页中说,egrep,fgrep和rgrep已被弃用,建议使用其等效形式“ grep -E”,“ grep -F”和“ grep -r”。
rsuarez

相反,awk '{print $1$5}'最好使用awk '{print $1$NF}',以便在某些日期用空格格式化的语言环境中不会中断(例如Jun 3而不是2017-06-03)。这里也是一个版本,通知特定的用户,而不是所有用户:gist.github.com/shvchk/ba2f0da49bf2f571d6bf606d96f289d7
舍夫丘克

1
使用grep -E并添加/bin到路径后,可以在Ubuntu上出色地工作(请参见编辑)。如果您反对,可以随时还原
serv-inc

3

我也需要一些系统范围的通知。这是我的解决方案。它扫描/ proc以查找所有会话总线,然后在每个会话总线上执行通知发送(每条总线一次)。所有参数均未更改地传递给实际的通知发送。

#!/bin/bash

/bin/grep -sozZe '^DBUS_SESSION_BUS_ADDRESS=[a-zA-Z0-9:=,/-]*$' /proc/*/environ \
| /usr/bin/php -r '
        $busses = array();
        array_shift($argv);
        while($ln = fgets(STDIN)) {
                list($f, $env) = explode("\0", $ln, 2);
                if (file_exists($f)) {
                        $user = fileowner($f);
                        $busses[$user][trim($env)] = true;
                }
        }
        foreach ($busses as $user => $user_busses) {
                foreach ($user_busses as $env => $true) {
                        if (pcntl_fork()) {
                                posix_seteuid($user);
                                $env_array = array("DBUS_SESSION_BUS_ADDRESS" => preg_replace("/^DBUS_SESSION_BUS_ADDRESS=/", "", $env));
                                pcntl_exec("/usr/bin/notify-send", $argv, $env_array);
                        }
                }
        }
' -- "$@"

1

在Ubuntu 16.04中,我希望从crontab以root身份运行的脚本发出通知。设置环境变量后,sudo -u $user由于某种原因sh -c "..." $user没有起作用,但是起作用了。

因此,我现在使用此功能:

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 
}

如何找到DBUS_SESSION_BUS_ADDRESS变量可能取决于您的分布。在Ubuntu 16.04中,它位于中/run/user/$UID/dbus-session,可以简单地获取。id -u在上面的函数中用于从中返回的用户名获取UID who


如何使用它?你能帮助我吗?
elgolondrino

0

这是Andy脚本的更新:它确定DBUS_SESSION_BUS_ADDRESS在Centos 7上不起作用的方式。who由于某些原因,该命令未列出某些会话,因此我解析了ps aux输出。该脚本假定用户使用X2GO(nxagent)登录,但是在其他情况下调整起来应该很简单。

#!/bin/bash
PATH=/usr/bin:/bin
NOTIFY_ARGS='-u critical "Shutdown notice" "THE SYSTEM IS GOING DOWN TODAY AT 23:00.\nWe recommend you to save your work in time\!" -i /usr/share/icons/Adwaita/32x32/devices/computer.png -t 28800000'

function extract_displays {
    local processes=$1
    processes=$(printf '%s\n' "$processes" | grep -P "nxagent.+:\d+")
    ids=$(printf '%s\n' "$processes" | grep -oP "\W\K:(\d)+")
    echo $ids
}


function find_dbus_address {
    local name=$1
    PID=$(pgrep 'mate-session' -u $name)
    if [ -z "$PID" ]; then
        PID=$(pgrep 'gnome-session' -u $name)
    fi
    if [ -z "$PID" ]; then
        PID=$(pgrep 'xfce4-session' -u $name)
    fi

    exp=$(cat /proc/$PID/environ | grep -z "^DBUS_SESSION_BUS_ADDRESS=")
    echo $exp
}

PROCESSES=$(ps aux)
DISPLAY_IDS=$(extract_displays "$PROCESSES")
echo "Found the following DISPLAYS: $DISPLAY_IDS"
for DISPLAY in $DISPLAY_IDS; do
    NAME=$(printf '%s\n' "$PROCESSES" | grep -P "nxagent.+$DISPLAY" | cut -f1 -d ' ')
    DBUS_ADDRESS=$(find_dbus_address $NAME)
    echo "Sending message to NAME=$NAME DISPLAY=$DISPLAY DBUS_ADDRESS=$DBUS_ADDRESS"
    echo "NOTIFY_ARGS=$NOTIFY_ARGS"
    eval sudo -u ${NAME} DISPLAY=${DISPLAY} ${DBUS_ADDRESS} PATH=${PATH} notify-send $NOTIFY_ARGS
done

-1
users=$(who | awk '{print $1}')

for user in $users<br>
do
        DISPLAY=:0 sudo -u $user notify-send "hello!!"
done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.