如何自动将 tmux 窗口重命名为别名命令

如何自动将 tmux 窗口重命名为别名命令

我已经设置了多个别名来 ssh 到不同的服务器

alias sa="ssh user1@server1"
alias sb="ssh user2@server2"
alias sc="ssh user3@server3"

当我从 tmux 运行这些命令时,所有窗口都重命名为ssh,而不是sasbsc。如何让别名显示为窗口名称而不是实际进程名称?

编辑:使用 macOS 12.2.1、tmux 3.2a、zsh 5.8

答案1

初步说明

  • 部分分析(如下)主要适用于 Linux。
  • 该解决方案已在 Kubuntu 21.10 中测试并发布。直到那时,OP 才透露他们使用的是 macOS;结果发现该解决方案在 macOS 上不起作用。
  • 我决定(暂时)不删除答案,但请注意,它在非 Linux 操作系统上可能会或可能不会起作用。

总结

alias sa='bash -c '\''exec -a "$0" ssh user1@server1 "$@"'\'' sa'

或者更好的(?)unalias sa如果需要的话,首先):

sa() {
   bash -c 'exec -a "$0" ssh user1@server1 "$@"' sa "$@"
}

分析——你可以做什么tmux

tmux默认情况下,根据活动窗格中正在运行的可执行文件更改窗口的名称。这由automatic-rename选项

automatic-rename [on | off]
控制自动窗口重命名。启用此设置后,tmux将使用 指定的格式自动重命名窗口。如果在创建时使用或指定名称,或者稍后使用或使用终端转义序列automatic-rename-format指定名称,则会自动为单个窗口禁用此标志。[…]new-windownew-sessionrename-window

我假设您希望此设置保持启用状态。

您可以根据需要重命名窗口 ( ),并在调用 之前或之后tmux rename-window sa重新启用设置 ( ) 。但是:tmux set-option -w automatic-rename onssh

  • 如果在 之前ssh,则选择同一窗口中的另一个窗格将使其automatic-rename完成其工作(您可能预料到这一点),但选择带有 的窗格ssh会将窗口重命名为ssh,而不是sa
  • 如果在之后ssh,则选择同一窗口中的另一个窗格(ssh完成之前)将不会重命名该窗口。

可能还有其他缺点;这两种方法都不完美。还请注意,如果您set-option -w automatic-rename在多个窗格中使用,则一个窗格中的命令可能会tmux以不同于另一个窗格试图强加的方式配置窗口。来回更改设置并不是一个好的解决方案。

一个好的解决方案是自动将tmux设置sa为标题。我们需要一种方法来告诉tmux使用字符串sa而不是ssh。为此,我们需要了解如何tmux知道ssh您正在运行。


分析——tmux知道什么

tmux为您的 shell 提供终端。当 shell 生成时ssh,shell 通过将进程组设置为ssh前台进程组,将自身置于后台。当ssh退出时,作为父进程的 shell 会做出反应,通过将其进程组设置为前台进程组,将自身置于前台。这种“玩弄”的主要目的是使作业控制工作(后台进程的标准输入仍然连接到终端,以防万一fg;但无法“窃取”输入,直到它们的进程组成为前台进程组)。

进程 (或其他进程) 能够tmux知道前台可执行文件的名称,这只是此机制的一个有用的副作用。了解它提供的终端的前台进程组,tmux可以找到作为组长进程的进程并自动调整标题。

shell 不会直接告知tmux正在执行什么命令。如果是这种情况,一个足够聪明的 shell 可以传递sa而不是ssh。您的 shell 决定哪个进程组(以及何时)成为与终端(tmux上下文中:窗格)关联的前台进程组。通过监视这一点,tmux可以在窗格中找到前台进程。

别名不是进程。每个别名都运行一个名为 的可执行文件sshtmux无法知道您使用的别名。这就是为什么您的窗口被重命名为ssh,无论您使用哪个别名。


解决方案

事实证明,前台可执行文件的名称对 来说并不重要tmux。重要的是 中第一个(有人会说是“第零个”)以空字符结尾的条目/proc/PID/cmdline。通常(按照惯例)它是可执行文件的路径或其名称;但实际上它可以是任何东西。

有多种方法可以运行ssh(或其他方式)并指定自定义字符串。请参阅设置线程/proc/PID/cmdline。在 Bash 中exec -a …可以做到这一点。但不要直接从交互式 shell 调用它,exec因为代替shell 中的内容与您想要运行的内容相同。因此您需要一个额外的内容bash来替换它。例如:

bash -c 'exec -a "foo" sleep 300'

在 shell 内部调用上述命令tmux将把tmux窗口重命名为foo(如果automatic-renameon;则可能需要等待几秒钟)。

使用此方法的基本别名是:

alias sa='bash -c "exec -a sa ssh user1@server1"'

我注意到您的别名与附加参数配合得很好。我的意思是,例如,您的sa sleep 30别名扩展为在服务器上ssh user1@server1 sleep 30运行的合理命令sleep 30。我们的别名尚不支持这一点。这是修复方法:

alias sa='bash -c '\''exec -a sa ssh user1@server1 "$@"'\'' whatever'

就像whatever第二sh中的第二个 sh 是什么sh -c 'some shell code' sh我个人更喜欢传递所需的名称,而不是whatever

alias sa='bash -c '\''exec -a sa ssh user1@server1 "$@"'\'' sa'

或者也许(见干燥):

alias sa='bash -c '\''exec -a "$0" ssh user1@server1 "$@"'\'' sa'

注意sa结尾处不需要匹配别名的名称。这里匹配是因为你想要这个。

别名有点复杂。不过,它还是有用的这超出了别名的用途。等效函数可能被认为更优雅。这是函数(unalias sa如果需要,则首先执行):

sa() {
   bash -c 'exec -a "$0" ssh user1@server1 "$@"' sa "$@"
}

笔记

  • exec -a不可移植,它在 Bash 中有效,但在纯 中无效sh。别名本身(或函数本身)在任何类似 POSIX 的 shell 中仍然有效。bash无论您的主 shell 是什么,它都会显式调用。
  • 有其他工具可以读取/proc/PID/cmdline或等效信息。我们的解决方案可能会误导他们或使用它们的用户。例如,将在列中ps -eo pid,comm,command打印,但在列中。sshcommsacommand

相关内容