Tampilkan pemberitahuan di semua tampilan X yang berjalan

16

Menggunakan baris perintah, saya ingin menampilkan pemberitahuan di setiap tampilan X yang sedang berjalan. (dan menjalankan konsol)

Sesuatu seperti:

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

Apakah ada program yang akan melakukan ini? Jika tidak, dapatkah ini diimplementasikan dengan bash?

Stefan
sumber
1
Untuk orang-orang yang datang ke sini bertahun-tahun kemudian, ada fungsi notify_all sederhana dalam jawaban ini yang berfungsi di Ubuntu 16.04, dan dapat digunakan dalam skrip yang dimulai oleh root.
mivk

Jawaban:

16

Anda dapat mengirim pesan ke semua konsol dengan dinding perintah.

Untuk mengirim notifikasi di bawah X ada notify-send yang mengirimkan notifikasi kepada pengguna saat ini pada tampilan saat ini. (Dari pertanyaan Anda, saya kira Anda sudah tahu yang ini.) Anda dapat membangun ini dengan beberapa skrip bash. Pada dasarnya Anda harus mencari tahu pengguna mana yang X-Display. Setelah Anda mendapatkan info ini, Anda dapat menggunakan notify-send seperti ini:

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

Di mana fschmitt adalah pengguna pada tampilan 0. Anda dapat mengurai output dari perintah "siapa" untuk menemukan semua pengguna dan tampilan mereka. Outputnya terlihat seperti ini

[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)

Anda tahu, ada dua pengguna yang menjalankan sesi X, markmerk3 pada tampilan 0 dan seamonkey pada tampilan 1. Saya pikir Anda perlu memahami untuk tty [0-9] * kemudian memastikan bahwa pada akhir baris ada (: [0 -9.] *) Untuk menyingkirkan login konsol dan mengekstrak id tampilan dari string di antara tanda kurung.

fschmitt
sumber
2
Perintah whomemberi tahu Anda siapa yang masuk dan di mana X menampilkan login itu. Anda mungkin harus sedikit memfilternya.
tante
1
Meskipun mungkin lebih baik menggunakan loop dalam skrip shell, Anda selalu bisa melakukan sesuatu seperti itu who | awk '/\(:[0-9]+\)/ {gsub("[:|(|)]","");print "DISPLAY=:"$5 " sudo -u " $1 " notify-send \"Message\""}' | bash. Juga, Anda mungkin ingin melihat unix.stackexchange.com/questions/1596/…
Steven D
8

Utas ini agak lama, maaf, tapi saya harap saya masih bisa menambahkan sesuatu yang berguna untuk topik ini. (juga Josef Kufner menulis naskah yang bagus, itu hanya sedikit terlalu lama untuk seleraku, dan itu menggunakan PHP)

Saya juga membutuhkan alat seperti yang dijelaskan dalam pertanyaan awal (untuk mengirim pesan ke semua pengguna X yang aktif). Dan berdasarkan jawaban di sini, saya menulis skrip bash-only kecil ini, yang mencari pengguna-X yang aktif (menggunakan 'siapa'), dan kemudian menjalankan notifikasi-kirim untuk setiap pengguna yang aktif.

Dan yang terbaik: Anda dapat menggunakan skrip saya persis seperti "beri tahu-kirim", dengan semua parameternya! ;-)

beri tahu-kirim-semua:

#!/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

Salin kode di atas ke dalam file bernama "notify-send-all", buat itu dapat dieksekusi dan salin ke / usr / local / bin atau / usr / bin (sesuka Anda). Kemudian jalankan mis. Sebagai root di sesi konsol seperti ini:

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

Saya menggunakannya beberapa bulan sekarang, pada mesin yang berbeda, dan sejauh ini tidak ada masalah, dan saya sudah mengujinya dengan desktop MATE dan Cinnamon. Juga berhasil menjalankannya di dalam cron dan anacron.

Saya menulis skrip ini untuk / di bawah ArchLinux, jadi tolong beri tahu saya jika Anda mengalami masalah pada distribusi atau desktop Linux lain.

Andy
sumber
|egrep?? Apakah egrep sebuah perintah?
Sw0ut
@ Sw0ut, egrep memang sebuah perintah. Tetapi dalam halaman manual grep (1) mengatakan bahwa egrep, fgrep dan rgrep sudah tidak digunakan lagi, dan penggunaan bentuk-bentuk ekivalennya "grep -E", "grep -F" dan "grep -r" direkomendasikan.
rsuarez
Alih-alih awk '{print $1$5}'lebih baik digunakan awk '{print $1$NF}', sehingga tidak merusak beberapa lokal tempat tanggal diformat dengan spasi (misalnya, Jun 3bukan 2017-06-03). Berikut ini juga versi untuk memberi tahu pengguna tertentu dan bukan semua pengguna: gist.github.com/shvchk/ba2f0da49bf2f571d6bf606d96f289d7
Shevchuk
1
Bekerja dengan baik di Ubuntu setelah menggunakan grep -Edan menambahkan /binke path (lihat edit). Jangan ragu untuk kembali jika Anda keberatan
serv-inc
3

Saya membutuhkan ini juga untuk beberapa notifikasi seluruh sistem. Ini solusinya. Ini memindai / proc untuk menemukan semua bus sesi dan kemudian mengeksekusi pemberitahuan-kirim pada masing-masing (sekali per bus). Semua argumen diteruskan tidak berubah ke notifikasi-kirim nyata

#!/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);
                        }
                }
        }
' -- "$@"
Josef Kufner
sumber
1

Di Ubuntu 16.04, saya ingin pemberitahuan dari skrip yang dijalankan sebagai root dari crontab. Setelah mengatur variabel lingkungan, sudo -u $usertidak berhasil karena suatu alasan, tetapi sh -c "..." $userberhasil.

Jadi saya sekarang menggunakan fungsi ini:

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 
}

Cara menemukan variabel DBUS_SESSION_BUS_ADDRESS mungkin tergantung pada distribusi Anda. Di Ubuntu 16.04, ia berada di /run/user/$UID/dbus-session, yang hanya dapat bersumber. id -udigunakan dalam fungsi di atas untuk mendapatkan UID dari nama pengguna yang dikembalikan oleh who.

mivk
sumber
Bagaimana cara menggunakannya? Bisakah kamu membantuku?
elgolondrino
0

Berikut ini adalah pembaruan skrip Andy: Cara itu menentukan DBUS_SESSION_BUS_ADDRESStidak bekerja pada Centos 7. Juga whoperintah tidak mencantumkan beberapa sesi untuk beberapa alasan, jadi saya parsing ps auxoutput sebagai gantinya. Skrip ini mengasumsikan pengguna login menggunakan X2GO ( nxagent), tetapi harus mudah disesuaikan untuk kasus lain.

#!/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
jpf
sumber
-1
users=$(who | awk '{print $1}')

for user in $users<br>
do
        DISPLAY=:0 sudo -u $user notify-send "hello!!"
done
Vicent
sumber