自动给tmux现场做简单的备胎

原文请猛戳:http://galoisplusplus.gitcafe.com/blog/2014/02/23/take-tmux-snapshots-automatically/

前段时间学校的EECS楼发生火灾,最近隔三差五停电检修,打断我在server上跑的实验。 而且我习惯上用tmux开多个session和window,一遇到停电我的tmux现场就悲剧了。 复电重开机之后要把tmux现场手动重新建好也很麻烦,于是我就挤出一点时间琢磨着写个简单的script去自动保存和重载tmux的副本。

首先我对tmux现场的定义其实还蛮简单的——我希望能记录tmux的session和window的名称、每个pane当前在什么路径以及在跑什么程序。 这些在tmux的man page中都有相应的内建变量(Variable)可以提供,这里谨列举如下:

session_name Name of session
window_name Name of windown
pane_current_path Current path if available
pane_current_command Current command if available

有了这么给力的内建变量,我们很快就可以写出保存tmux现场的命令了:

1

$ tmux list-windows -a -F"#{session_name} #{window_name} #{pane_current_command} #{pane_current_path}"

简单解释一下,tmux的list-windows命令顾名思义就是列举窗口,其后可以接以下参数:

1

list-windows [-a] [-F format] [-t target-session]

[-a]自然是指所有窗口;[-F]指定输出格式,我上面那条命令是依次列出session名、window名、pane当前执行程序和pane当前路径,并以空格隔开;[-t]指定输出在哪个session,默认是当前的session,这里我没有用,反正输出结果最后是要被重定向到文件的。

那么有了tmux现场的snapshot之后,我们应该如何恢复现场呢?

首先当然是解析snapshot中的信息,得到\${session_name}、\${window_name}、\${pane_current_command}、\${pane_current_path}的信息。

接下来是把各个session和window恢复好。

tmux稍显繁琐的地方是:用new-window创建新的window时必须指定现有的session,假如session不存在,该命令不会创建session,而会报错结束。 所以,当我们恢复每次恢复一个window时,需要先知道它所在的session是否存在:如果存在,则用new-window直接在该session上创建window;如果不存在,则需要用new-session来创建session,session创建后会有一个默认的窗口,我们就把所要恢复的窗口的环境设定到默认窗口上。

判断session是否存在可以用tmux的has-session命令:

1

2

3

4

has-session [-t target-session]  (alias: has)  Report an error and exit with 1 if the specified session does not exist. If it does exist,  exit with 0.

如果session存在,上述命令的退出码为0,否则则为1。这在bash中只需执行:

1

$ tmux has-session -t "${session_name}" 2>/dev/null

之后判断$?即可。

假如session不存在,则我用以下命令创建新session,并设默认窗口名为当前所要恢复的窗口的名称:

1

$ tmux new-session -d -s "${session_name}" -n ${window_name}

假如session已存在,则我用以下命令在该session上恢复原来的窗口:

1

$ tmux new-window -d -t ${session_name} -n "${window_name}"

下面我们要让每个pane都回到原来的路径,我的想法是直接把一个cd的shell命令送到当前的pane,并执行这条命令。 在tmux中的解决方案稍微有点小技巧,关键是用send-keys命令把该shell命令和一个ENTER送到该窗口,这种方式就像直接在窗口输入上述shell命令再按回车键执行。 以下是跳转回原来路径的tmux完整命令:

1

$ tmux send-keys -t "${session_name}:${window\_name}" "cd ${pane_current_path}" ENTER

恢复每个pane原来在执行的命令也可以用上述同样的方法。 可惜的是,tmux尽管提供了\${pane_current_command}的内建变量,但这个变量却无法提供精确的信息。例如执行的命令是类似exe arg1 arg2带参数的形式,\${pane_current_command}只会给出exe而无法检测到任何参数。因此,当我们重载tmux现场时直接执行\${pane_current_command}可能会带来问题。我采取的方案很简单,在每个终端窗口用\${pane_current_command}给一个提示,让使用者自行判断恢复后应该执行什么命令。 给出提示的tmux命令可以和之前恢复原路径的命令合并在一起:

1

$ tmux send-keys -t "${session_name}:${window\_name}" "cd ${pane_current_path}; echo \"Hint: last time you are executing '${pane_current_command}'.\"" ENTER

最后,我想把这个script加到crontab中,所以我需要让它自动判断当前应该做snapshots还是从snapshots恢复tmux现场。 我采用的方式也比较简单,通过ps看看当前有没有tmux进程:没有的话说明需要恢复,此时先执行tmux start-server;有的话则进行snapshots的保存。

完整的脚本如下(或者参考我的gist):

(tmuxEnvSaver.sh) download
1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

#!/bin/bash tmuxSnapshot=/.tmux_snapshot tmuxEXE=/usr/local/bin/tmux save_snap() {  ${tmuxEXE} list-windows -a -F"#{session\_name} #{window\_name} #{pane\_current\_command} #{pane\_current\_path}" > ${tmuxSnapshot} } restore_snap() {  ${tmuxEXE} start-server  while IFS=' ' read -r session_name window_name pane_current_command pane_current_path  do  ${tmuxEXE} has-session -t "${session_name}" 2>/dev/null  if [ $? != 0 ]  then  ${tmuxEXE} new-session -d -s "${session_name}" -n ${window_name}  else  ${tmuxEXE} new-window -d -t ${session_name} -n "${window_name}"  fi  ${tmuxEXE} send-keys -t "${session_name}:${window\_name}" "cd ${pane_current_path}; echo \"Hint: last time you are executing '${pane_current_command}'.\"" ENTER  done < ${tmuxSnapshot} } ps aux|grep -w tmux|grep -v grep if [ $? != 0 ] then restore_snap else save_snap fi 

我设了如下的crontab:

1

2

3

* * * * * echo "`date`: tmuxEnvSaver is running" >> /tmp/cron-tmux.log 2>&1 * * * * * /home/shuai/tmuxEnvSaver.sh >> /tmp/cron-tmux.log 2>&1 @reboot /home/shuai/tmuxEnvSaver.sh >> /tmp/cron-tmux.log 2>&1

这样就可以自动保存和重载简单的tmux现场了。

当然,tmux还有很多内建变量,因此这个简单的脚本还可以继续改进,让snapshots的信息更丰富,偶还是等下次有空再折腾吧XD

后续

我搜到一些相关的文章,有用perl来写类似脚本的,可供大家参考:

[1]Preserving (some) session state with tmux and bash

[2]Resurrecting tmux Sessions After Reboot

你可能感兴趣的:(简单)