cron 在终端运行程序或运行图形应用


  • 需求:利用 cron,定时在终端运行程序,以查看程序输出;或运行图形应用,如 gedit, xclock

cron 运行于非交互 shell,没有附属的终端,也独立于图形桌面。那么有没有办法实现上述需求呢?

我首先想到的是 gnome-terminal.

gnome-terminal -x do-job-command

可以实现在打开的终端中执行 do-job-command.

所以我尝试 cron 能不能打开 gnome-terminal,
cron 不是运行于 X.org server 下,因此它不知道必要的环境变量以打开 X.org server 应用,这些环境变量必须被定义。这里我们定义 DISPLAY.
加入计划任务 crontab -e

* * * * * export DISPLAY=:0 && /usr/bin/gnome-termimal
or
* * * * * env DISPLAY=:0 /usr/bin/gnome-termimal

无法打开(参考资料里说可以,可能跟系统有关),mate-terminal, x-terminal-emulator 也打不开,最终确认 xterm 可以。

实现方式 1

所以,计划任务可以是

0 4 * * * env DISPLAY=:0 /usr/bin/xterm -e do-job-command

实现方式 2

上述 gnome-terminal 在 cron 打不开,查看 cron 发送的邮件,出错信息如下:

Error constructing proxy for org.gnome.Terminal:/org/gnome/Terminal/Factory0:
+Could not connect: Connection refused

解决办法是:

dbus-launch gnome-terminal

dbus-launch 启动一个 Desktop Bus broker,供 gnome-terminal 和 gnome-terminal-server 通信。

最终,计划任务为

0 4 * * * export DISPLAY=:0 && /usr/bin/dbus-launch /usr/bin/gnome-terminal -x do-job-command

需要强调,dbus-launch 每次都会开启一个 dbus-daemon 进程,即使计划任务退出,该进程继续存在。如果计划任务频繁开启,就会产生很多 dbus-daemon 进程。所以,应当在计划任务退出时 kill 相应的 dbus-daemon 进程。注意,系统登录后也有自己的 dbus-daemon 进程,不要 kill 错了。

参考:https://unix.stackexchange.com/questions/344617/error-constructing-proxy-when-trying-to-launch-gnome-terminal-as-root

实现方式 3 (2 的改进)

方式 2 开启一个新的 Desktop Bus broker ,有上述一些缺陷。可以利用系统登录后现有的 Desktop Bus broker.

$ echo $DBUS_SESSION_BUS_ADDRESS 
unix:abstract=/tmp/dbus-FzlL8v7efh

计划任务为

* * * * * env DISPLAY=:0 env DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-FzlL8v7efh /usr/bin/gnome-terminal -x do-job-command

实现方式 4

* * * * * DISPLAY=:0 /usr/bin/exo-open --launch TerminalEmulator do-job-command

出错:Failed to execute default Terminal Emulator. Input/output error.

TerminalEmulator 即 x-terminal-emulator

$ sudo update-alternatives --config x-terminal-emulator 
There are 6 choices for the alternative x-terminal-emulator (providing /usr/bin/x-terminal-emulator).

  Selection    Path                             Priority   Status
------------------------------------------------------------
* 0            /usr/bin/gnome-terminal.wrapper   40        auto mode
  1            /usr/bin/gnome-terminal.wrapper   40        manual mode
  2            /usr/bin/koi8rxterm               20        manual mode
  3            /usr/bin/lxterm                   30        manual mode
  4            /usr/bin/mate-terminal.wrapper    30        manual mode
  5            /usr/bin/uxterm                   20        manual mode
  6            /usr/bin/xterm                    20        manual mode

Press  to keep the current choice[*], or type selection number:

可知,调用的是 gnome-terminal。所以,同方式 3,提供 DBUS_SESSION_BUS_ADDRESS 变量即可。
最终的计划任务为

* * * * * DISPLAY=:0 DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-MsQxIw6b3X /usr/bin/exo-open --launch TerminalEmulator do-job-command

实现方式 5

将程序输出重定向到已经打开的终端
tty 查看当前终端
运行级别 3 下是 /dev/tty1-6
运行级别 5 ,或者 ssh 登录,是 /dev/pts/*
计划任务为

* * * * * do-job-command > /dev/pts/17

关于终端与控制台,参考

参考资料

  • https://askubuntu.com/questions/309853/getting-output-from-a-cron-job-on-the-terminal
  • https://wiki.archlinux.org/index.php/Cron#Running_X.org_server-based_applications
  • https://superuser.com/questions/351528/open-a-terminal-from-a-crontab
  • https://unix.stackexchange.com/questions/325346/how-to-execute-the-command-in-cronjob-to-display-the-output-in-terminal
  • https://askubuntu.com/questions/1020965/how-do-i-set-cron-to-send-emails