screen中自动修改窗口标题,一般配置方案使用ANSI转义序列(或配合shelltitle),但是看了网上搜到的配置不太满意,尤其对于fg命令恢复进程到前台的情况。这里给出一个比较简单的配置,支持fg将进程恢复到前台执行后正确设置screen窗口标题。同时这个配置也便于灵活自定义修改特定命令的窗口标题设置。

简单方式

screen窗口标题设置的细节可以参考man screen中”TITLES”章节,这里关注设置窗口标题的ANSI转义序列<esc>kNAME<esc>\,这个转义序列可以由应用程序输出,将窗口标题设置为NAME。

对于shell,还有一种启发式的方法。将窗口标题设置为"search|name"(一般在.screenrc中使用shelltitle指令),并在shell提示符中输出一个空名字的转义序列(也就是<esc>k<esc>\),shell提示符末尾要与前面的search部分相同。这样screen会在接收到空名字转义序列时清空窗口标题并准备接收下一个指令,换行时会使用前面提到的search搜索提示符末尾,当这个末尾被找到,会使用之后的第一个单词作为命令名设置给窗口标题。如果这个命令名字以'!''%''^'开头,会有额外的处理。

比如当shell提示符末尾为"$ "时,使用如下设置:

  • shelltitle设置为"$ |bash"
  • 配置环境变量PROMPT_COMMAND='printf "\033k\033\134"'

像开头提到的,单纯这种方式,对于fg恢复到前台的进程只会将窗口标题设置为fg。因此这里给出另一种配置方式,不依赖shelltitle的"search|name",只是使用bash的trap和环境变量PROMPT_COMMAND

改进方式

下面这段配置需要添加到~/.bashrc中或由其加载,因为screen默认创建的和C-a c创建的窗口是交互式非登录shell,只会加载~/.bashrc文件,不会加载~/.bash_profile~/.profile,可以参考bash 调用方式与配置文件

~/.bashrc
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 设置screen窗口标题,这个方式不依赖screenrc中的shelltitle
if [ -n "$STY" ]; then # 仅在 screen 中生效
# 获取后台作业命令
__get_fg_cmd() {
local job_id
if [[ "$*" =~ ([0-9]+) ]]; then
job_id="${BASH_REMATCH[1]}"
else
job_id="%+"
fi
jobs "$job_id" | awk '{$1=$2=""; sub(/^ */, ""); print}'
}

__get_vim_cmd() {
local input="$*"
if [[ $input =~ [[:space:]]+([^-][^[:space:]]*) ]]; then
local name="${BASH_REMATCH[1]}"
name=$(basename "$name")
printf '[vim]%s' "$name"
else
printf "vim"
fi
}

__get_cmd() {
local i
local _command
local _sudo=0
_command="$BASH_COMMAND"
for i in {0..4}; do
# 清除前缀空格
_command="${_command#"${_command%%[![:space:]]*}"}"
case "$_command" in
"fg"|"fg "*)
_command=$(__get_fg_cmd $_command)
;;
"vim"|"vim "*)
_command=$(__get_vim_cmd $_command)
# vim不继续处理
break
;;
"sudo "*)
# sudo 删除
_command=$(printf '%s' "$_command" | awk '{$1=""; sub(/^ /, ""); print}')
_sudo=1
;;
*)
# 提取命令名
_command=$(printf '%s' "$_command" | awk '{print $1}' | sed 's#^.*/##')
# 跳出循环
break
;;
esac
# 循环结束未提取完
if [ $i -eq 4 ];then
# 提取命令名
_command=$(printf '%s' "$_command" | awk '{print $1}' | sed 's#^.*/##')
fi
done
if [ $_sudo -ne 0 ];then
printf '#%s' "$_command"
else
printf '%s' "$_command"
fi
}

# 设置标题函数
__set_screen_title() {
local cmd=$(__get_cmd)
if [ x${cmd} != x ];then
printf "\033k${cmd}\033\134"
fi
}

# 设置触发器
trap '__set_screen_title' DEBUG
# 一些发行版会使用PROMPT_COMMAND改变终端窗口标题,保留原ANSI序列。增加返回screen默认标题bash
if [ "x$PROMPT_COMMAND" == "x" ];then
PROMPT_COMMAND="printf '\033kbash\033\134'"
else
PROMPT_COMMAND="$PROMPT_COMMAND;printf '\033kbash\033\134'"
fi
fi

PS:.screenrc中主要也就是配置一下term和hardstatus,左边窗口列表,右边显示时间,方便对照日志中时间、主机时间、办公电脑时间,可有可无吧。

~/.screenrc
1
2
3
4
5
# 一些发行版会根据TERM配置PROMPT_COMMAND
term xterm

hardstatus alwayslastline
hardstatus string "%-w%{= BW}%50>%n %t%{-}%+w%< %=%c %Y-%M-%d [%H]"