Как программно найти текущее значение DISPLAY, когда DISPLAY не установлен? (для использования в crontab)

У меня есть скрипт, который запускается через cron для смены обоев рабочего стола. Я могу заставить его работать, экспортируя переменную DISPLAY.

Но проблема меня беспокоит, что скрипт должен работать на всех системах. Как найти значение DISPLAY, установленное X-сервером, без использования $DISPLAY?

Итак, как я могу найти правильное значение для DISPLAY программно. Я могу заставить его работать только тогда, когда DISPLAY=:1, Установка его в ":0" завершает работу скрипта с

No protocol specified
Cannot open display.

3 ответа

Решение

Вы не можете точно. Вы должны сделать предположения.

Притворись, что ты cron и на секунду вы столкнулись с наихудшим сценарием: в систему вошли несколько пользователей, и каждый пользователь запускает несколько X-сеансов. Вы должны будете угадать пользователя (достаточно просто, мы выполняем его crontab) и один из сеансов X этого пользователя.

Если мы хотим предположить, что пользователь запускает один и только один сеанс X из tty, и получить этот сеанс $DISPLAY значение, которое мы можем использовать w:

USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
user     tty1                      16:32    7:15   0.21s  0.19s -zsh
user     tty2     :1               15:52   48:13   2:17   0.08s /sbin/upstart
user     pts/3    :1               16:19    0.00s  0.66s  0.00s w

Например, здесь я вошел на tty1, tty2 (где я запускаю сеанс X) и на pts/3 (терминал, с которого я запускаю команду).

С небольшим разбором:

% w $(id -un) | awk 'NF > 7 && $2 ~ /tty[0-9]+/ {print $3; exit}'
:1

Итак, предполагая все вышесказанное:

0 0 * * * DISPLAY=$(w $(id -un) | awk 'NF > 7 && $2 ~ /tty[0-9]+/ {print $3; exit}') command

Сделаю cron выполнять command с $DISPLAY установить первый сеанс X, запущенный в tty $DISPLAY найденное значение для пользователя.

Более подробное обсуждение см. По https://unix.stackexchange.com/questions/17255/is-there-a-command-to-list-all-open-displays-on-a-machine

Я просто перечислю соответствующую информацию из этого ответа здесь:

Кажется, есть два простых способа найти экземпляры X-сервера, работающие в вашей системе.

  • w:
    w Команда выводит список всех открытых дисплеев. Вы можете использовать awk отфильтровать нужную вам информацию. Значения под FROM значения, соответствующие DISPLAY,

    ashhar@xenon:[/tmp/.X11-unix] 
    $ w
    21:18:24 up  3:39,  4 users,  load average: 0.31, 0.27, 0.30
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
    ashhar   tty2     :1               17:40    3:39m  6:11   0.08s /usr/bin/dunst
    ashhar   pts/0    :1               17:41    3:36m  0.13s  0.05s vim .i3/config
    ashhar   pts/1    :1               18:07    0.00s  1.44s  0.00s w
    ashhar   pts/2    :1               18:15    9:59   0.79s  0.79s bash
    
  • Локальные дисплеи соответствуют розетке в /tmp/.X11-unixтак что мы можем просто сделать:

    cd /tmp/.X11-unix && for x in X*; do echo ":${x#X}"; done
    

Вдохновленный ответом Ашхара Хасана, я бы использовал (протестировано на Ubuntu 23.04):

      export DISPLAY=$(
  find /tmp/.X11-unix/ -user $USER -type s -printf "%f" -quit | tr X :
)

Благодаря фильтрации по UID он будет работать даже при наличии нескольких вошедших в систему пользователей.

У меня есть сценарий X11wrap.sh, который позволяет мне запускать приложение X11 из системных сценариев, cron и т. д.

      #!/bin/sh

set -eu

export DISPLAY=$(
      find /tmp/.X11-unix/ -user $USER -type s -printf "%f" -quit | tr X :
)

# For applications that additionally require DBUS.
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus

exec "$@"

С его помощью я могу выдать следующий пример, и он правильно настроит систему X11:

      echo X11wrap.sh xeyes | at now
Другие вопросы по тегам