如何在 .tmux.conf 中编写 if 语句来为不同的 tmux 版本设置不同的选项?

How to write if statement in .tmux.conf to set different options for different tmux versions?

我有一个 .tmux.conf,我在安装了不同 tmux 版本的不同机器上使用它。

我想根据 tmux 版本设置不同的鼠标选项。 在一台机器上我有版本 2.0 在另一台 2.1.

我没听懂他的部分

if "[[(( $(tmux -V | cut -c 6-) < 2.1 ))]]" \
  "set -g mode-mouse on;" \
  "set -g mouse-resize-pane on;" \
  "set -g select-pane on;" \
  "set -g select-window on" "set -g mouse on"

当我获取文件时

$ tmux source-file .tmux.conf

我收到这条消息

.tmux.conf:12: unknown command: set -g mouse-resize-pane on

我所在的机器 运行 它的版本是 2.1 所以它不应该设置这四个选项。

我想在 运行ning tmux 2.0 或更低版本时设置四个选项,或者在 运行ning tmux 2.1 时设置一个选项。

这个bash语句有效

$ tmux -V
tmux 2.1
$ if [[(( $(tmux -V | cut -c 6-) < 2.1 ))]];then echo $?;else echo $?;fi
1

Tmux的if-shell可以用来查看ZSH版本

[[ `tmux -V | cut -d' ' -f2` -lt 2.1 ]]

检查 tmux 版本是否大于或等于 2.1。使用它我们可以根据 ZSH 版本设置鼠标命令。

if-shell "[[ `tmux -V | cut -d' ' -f2` -lt 2.1 ]]" \
    'set -g mode-mouse on; set -g mouse-resize-pane on; set -g mouse-select-pane on; set -g mouse-select-window on'

并为更高版本的 tmux 设置它:

if-shell "[[ `tmux -V | cut -d' ' -f2` -ge 2.1 ]]" \
    'set -g mouse on; set -g mouse-utf8 on'

if-shell 并不总是有效。相反,我使用 shell 脚本加载 tmux.conf 的正确版本:

在.tmux.conf:

run-shell "bash ~/.tmux/verify_tmux_version.sh"

在verify_tmux_version.sh中:

#!/bin/bash

verify_tmux_version () {
    tmux_home=~/.tmux
    tmux_version="$(tmux -V | cut -c 6-)"

    if [[ $(echo "$tmux_version >= 2.1" | bc) -eq 1 ]] ; then
        tmux source-file "$tmux_home/tmux_2.1_up.conf"
        exit
    elif [[ $(echo "$tmux_version >= 1.9" | bc) -eq 1 ]] ; then
        tmux source-file "$tmux_home/tmux_1.9_to_2.1.conf"
        exit
    else
        tmux source-file "$tmux_home/tmux_1.9_down.conf"
        exit
    fi
}

verify_tmux_version

更多详情: https://gist.github.com/vincenthsu/6847a8f2a94e61735034e65d17ca0d66

这有点仓促。 在 tmux 中执行此操作的正确方法(不依赖于外部 shell 脚本)结合了 Vincent 和 jdloft 的响应的功能。

tmux中的if-shell命令用作

if-shell [-bF] [-t target-pane] shell-command command [command]
               (alias: if)
    Execute the first command if shell-command returns success or the second command otherwise.  Before
    being executed, shell-command is expanded using the rules specified in the FORMATS section, including
    those relevant to target-pane.  With -b, shell-command is run in the background.

    If -F is given, shell-command is not executed but considered success if neither empty nor zero (after
         formats are expanded).

请注意,tmux shell-command 扩展将扩展形式为 #{pane_current_path} 的变量,否则将单独保留命令。

更重要的是,请注意 tmux uses /bin/sh -c to execute the shell command we specify. Thus, the command must be POSIX compliant,因此不保证 [[ 形式的测试是可移植的。现代 Ubuntu 和 Debian 系统,例如,符号链接 /bin/shdash.

我们想要 运行 一个 POSIX 兼容的 shell 命令来测试 tmux 版本和 returns 0 (true) 如果找到所需的版本。

if-shell '[ $(echo "$(tmux -V | cut -d" " -f2) >= 2.1" | bc) -eq 1 ]' \
    'command if true' \
    'command if false'

示例:

if-shell '[ $(echo "$(tmux -V | cut -d" " -f2) >= 2.1" | bc) -eq 1 ]' \
    'set -g mouse on; set -g mouse-utf8 on' \
    'set -g mode-mouse on; set -g mouse-resize-pane on; set -g mouse-select-pane on; set -g mouse-select-window on' 

这正确地处理了我们正在进行浮点运算的事实,因此 bc is required。此外,不需要 if/then/else/fi 构造,因为 [ 运算符会自行生成真值。

几个笔记

  • 继续到下一行的行不能有尾随注释,否则 tmux 将给出 "unknown command" 错误消息。
  • "command if false"可以省略
  • 可以使用 ;
  • 组合用于 true 或 false 的多个命令
  • 命令是 运行 在基础 shell 上使用 /bin/sh -c。不保证使用 [[ 或其他非 POSIX 语法的其他方法有效。

编辑:此答案的先前版本使用了 [[,它在不使用 bash 的系统上不起作用。替换为 [ 解决了这个问题。

基于 @ericx's answer and I put the following together which covers many of the listed incompatibilties 从 2.0 版开始:

# Version-specific commands [grumble, grumble]
# See: https://github.com/tmux/tmux/blob/master/CHANGES
run-shell 'tmux setenv -g TMUX_VERSION $(tmux -V | \
                           sed -En "s/^tmux[^0-9]*([.0-9]+).*//p")'


if-shell -b '[ "$(echo "$TMUX_VERSION < 2.1" | bc)" = 1 ]' " \
    set -g mouse-select-pane on; set -g mode-mouse on; \
    set -g mouse-resize-pane on; set -g mouse-select-window on; \
    set -g message-fg red; \
    set -g message-bg black; \
    set -g message-attr bright; \
    set -g window-status-bg default; \
    set -g window-status-fg default; \
    set -g window-status-current-attr bold; \
    set -g window-status-current-bg cyan; \
    set -g window-status-current-fg default; \
    set -g window-status-bell-fg red; \
    set -g window-status-bell-bg black; \
    set -g window-status-activity-fg white; \
    set -g window-status-activity-bg black"

# In version 2.1 "mouse" replaced the previous 4 mouse options
if-shell -b '[ "$(echo "$TMUX_VERSION >= 2.1" | bc)" = 1 ]' \
  "set -g mouse on"

# UTF8 is autodetected in 2.2 onwards, but errors if explicitly set
if-shell -b '[ "$(echo "$TMUX_VERSION < 2.2" | bc)" = 1 ]' \
  "set -g utf8 on; set -g status-utf8 on; set -g mouse-utf8 on"

# bind-key syntax changed in 2.4 -- selection / copy / paste
if-shell -b '[ "$(echo "$TMUX_VERSION < 2.4" | bc)" = 1 ]' " \
   bind-key -t vi-copy v   begin-selection; \
   bind-key -t vi-copy V   send -X select-line; \
   bind-key -t vi-copy C-v rectangle-toggle; \
   bind-key -t vi-copy y   copy-pipe 'xclip -selection clipboard -in'"

# Newer versions
if-shell -b '[ "$(echo "$TMUX_VERSION < 2.9" | bc)" = 1 ]' " \
   bind-key -T copy-mode-vi v   send -X begin-selection; \
   bind-key -T copy-mode-vi V   send -X select-line; \
   bind-key -T copy-mode-vi C-v send -X rectangle-toggle; \
   bind-key -T copy-mode-vi y   send -X copy-pipe-and-cancel 'xclip -selection clipboard -in'"

if-shell -b '[ "$(echo "$TMUX_VERSION >= 2.9" | bc)" = 1 ]' \
   "set -g message-style fg=red,bg=black; \
    set -g message-style bright; \
    set -g window-status-style          fg=default,bg=default; \
    set -g window-status-current-style  fg=default,bg=cyan,bold; \
    set -g window-status-bell-style     fg=red,bg=black; \
    set -g window-status-activity-style fg=white,bg=black"

我提出了一个关于 tmux 的非向后兼容性 here 的问题。总结是 tmux 开发者将不支持向后兼容性,他们也不会采用突出显示哪些版本包含重大更改的版本编号方案。

我向 support numeric comparators for %if which was implemented in v3.0 提出了一个问题。

在某些机器上,我使用双括号 ('[[') 语法得到了误报结果。所以我想出了一个使用 awk 的替代方法:

# Enable mouse for different versions of tmux
# (If 'awk' exits with status 0, 'if-shell' evaluates to true)
# tmux < v2.1:
if-shell "tmux -V | awk '{exit !( < \"2.1\")}'" \
    "setw -g mode-mouse on ; set -g mouse-select-pane on ; set -g mouse-resize-pane on ; set -g mouse-select-window on ;"
# tmux >= v2.1:
if-shell "tmux -V | awk '{exit !( >= \"2.1\")}'" \
    "set -g mouse on ;"

我也偶然发现不同 tmux 版本中的配置不匹配。在查看了此处和 this related question on SuperUser 中的所有解决方案后,我实施了以下变体:

# Version-specific configuration can be placed in ~/.tmux/${TMUX_VERSION}/*.conf
run-shell "for conf in ~/.tmux/$(tmux -V | cut -d' ' -f2)/*.conf; do tmux source-file \"$conf\"; done"

有了这个,version-specific 配置可以放入特定版本的(多个)配置片段中。这类似于@VincentHsu的解决方案,但是:

  • 它不需要外部 shell 脚本。
  • 它不分为固定版本范围(.../1.9 到 2.0/2.1...)。相反,可以使用 (sym)links 在多个 tmux 版本之间共享配置片段。
  • 它不会为一个版本硬编码单个文件名。通过为每个版本允许多个配置片段,部分可以在版本之间共享,而其他部分可以保留 version-specific。这应该提供最大的灵活性。
  • 原始 ~/.tmux.conf 中只有一个配置元素。其他解决方案,如来自@TomHale 的解决方案,为每个配置元素重复版本测试。

我已将建议的解决方案组合成一个可行的解决方案(在 tmux 1.8 和 2.7 上测试):

run-shell "tmux setenv -g TMUX_VERSION $(tmux -V | cut -c 6-)"

if-shell -b '[[ "$TMUX_VERSION" < "2.6" ]]' \
  "bind w choose-tree -u"

if-shell -b '[[ "$TMUX_VERSION" < "2.2" ]]' \
  "set -g status-utf8 on"

当前的最新版本是 2.9a,这与此处使用的许多直接比较不同。

我的替代方案使用 sort -V,它在处理版本比较方面更加稳健。

编辑:评论者指出 sort -V 在 BSD sort 中不可用,例如在本机 OSX 中。然而,这仍然是一个解决非纯数字版本的答案。

# ver >= 2.3
[ ! "$(printf "%s\n%s" "$TMUX_VERSION" "2.3" | sort -V | head -n1)" == "$TMUX_VERSION" ]' \
    "command"

# ver > 2.3
[ ! "$(printf "%s\n%s" "$TMUX_VERSION" "2.4" | sort -V | head -n1)" == "$TMUX_VERSION" ]' \
    "command"

# ver < 2.3
[ "$(printf "%s\n%s" "$TMUX_VERSION" "2.3" | sort -V | head -n1)" == "$TMUX_VERSION" ]' \
    "command"

这不是替代方案,而是对已接受答案的扩展 -(Tom Hale 的答案是最可靠和最完整的)。

为了减少 .tmux.conf 中的重复,您可以将 shell 测试字符串存储在 tmux env vars (available since 0.8 in 2008) 中并在 run-time 中插入它们以获得 if-shell:

# Use which sed pattern actually works in the accepted answers
run-shell 'tmux setenv -g TMUX_VERSION $(tmux -V | sed -En "...")'

# Setup condition checks
V33_GTE='[ "$(echo "$TMUX_VERSION >= 3.3" | bc)" = 1 ]' 
V33_LT='[ "$(echo "$TMUX_VERSION < 3.3" | bc)" = 1 ]'

# So on for other versions
# ...

### Example binding
# Toggle mouse
bind m set -g mouse on  \; display 'Mouse: ON'
bind M set -g mouse off \; display 'Mouse: OFF' 

# As of 3.1 you can annotate your keybindings
# As of 3.3 you can attach a note to existing bindings without setting the binding
# If running >= 3.3 , attach a note to the binding for `list-keys -N`

if "$V33_GTE" bind -N "Enable mouse mode" m
if "$V33_GTE" bind -N "Disable mouse mode" M

我最近 运行 遇到了这个问题,我的解决方案是编写一个“tmux-older-than”脚本:

#! /bin/bash

TMUX_VERSION="$(tmux -V | cut -d" " -f2)"
test 1 -eq "$( echo "$TMUX_VERSION < " | bc)"

这使得 tmux.conf 更具可读性:

if-shell 'tmux-older-than 3.0' \
        'bind-key ^P swap-window -t -1' \
        'bind-key ^P swap-window -d -t -1'

这个配置我用了很多年

  • 支持tmux 2.4tmux 2.4atmux next-3.3
  • 等版本
  • bc命令
  • 支持 Cygwin、macOS 和 Linux
# Tmux version before 2.4
if-shell -b '[ `tmux -V | cut -d" " -f2 | tr -d " |\-|.|[:alpha:]"` -lt 24 ]' \
  'bind-key -t vi-copy v begin-selection; \
    bind-key -t vi-copy Q cancel; \
    bind-key -t vi-copy Enter cancel'

# Tmux version 2.4 onwards
if-shell -b '[ `tmux -V | cut -d" " -f2 | tr -d " |\-|.|[:alpha:]"` -ge 24 ]' \
  'bind-key -T copy-mode-vi C-w   send-keys -X cancel; \
bind-key -T copy-mode-vi C-u   send-keys -X halfpage-up; \
bind-key -T copy-mode-vi C-j   send-keys -X halfpage-down; \
bind-key -T copy-mode-vi C-l   send-keys -X select-line'

tmux 3.0a

# execute a tmux command if a shell-command succeeded
# if-shell '[[ -z "$SSH_CLIENT" ]]' \     # use test or [ , instead of [[
# if-shell "[ $HOST == 'redmi14-leo' ]"   # can't work don't know why

if-shell '[[ -z "$SSH_CLIENT" ]]' \
    'source-file ~/.tmux.remote.conf'

较新版本的 tmux 支持更简洁的 if 条件。以下适用于 tmux 3.2a 版:

if-shell '[ "$(tmux -V)" = "tmux 3.2a" ]' {
    set -g status-bg red
    set -g status-fg yellow
}