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

I have a .tmux.conf which I use on different machines with different tmux versions installed.

I want to set different mouse options, depending on the tmux version. On one machine I have version 2.0 on the other 2.1.

I do not get his part right

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"

When I source the file

$ tmux source-file .tmux.conf

I get this message

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

The machine where I run it has version 2.1 so it shouldn't set the four options.

I want to set the four options when running tmux 2.0 or less or the one option when running tmux 2.1.

This bash statement works

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

Solution 1:

Based on @ericx's answer and @thiagowfx's answer I put the following together which covers many of the listed incompatibilties from version 2.0 onwards:

# 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]+).*/\1/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"

I raised an issue about the problems with tmux's non-backward-compatibility here. The summary is that the tmux devs will not support backward compatibility, nor will they adopt a version numbering scheme which highlights which versions contain breaking changes. 😢

I raised an issue to support numeric comparators for %if which was implemented in v3.0.

Solution 2:

if-shell doesn't always work. Instead, I use a shell script for loading the correct version of tmux.conf:

In .tmux.conf:

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

In 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

For more details: https://gist.github.com/vincenthsu/6847a8f2a94e61735034e65d17ca0d66

Solution 3:

This is kind of a hastle. The correct way to do this within tmux (not relying on an external shell script) combines features of both Vincent and jdloft's responses.

The if-shell command in tmux is used as

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).

Note that tmux shell-command expansion will expand variables of the form #{pane_current_path} but otherwise will leave the command alone.

More importantly, note that tmux uses /bin/sh -c to execute the shell command we specify. Thus, the command must be POSIX compliant, so tests of the form [[ are not guaranteed to be portable. Modern Ubuntu and Debian systems, for example, symlink /bin/sh to dash.

We want to run a POSIX compliant shell command that tests the tmux version and returns 0 (true) if the desired version is found.

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

Example:

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' 

This correctly deals with the fact that we are doing floating point arithmetic, so bc is required. Additionally, there is no need for an if/then/else/fi construct, as the [ operator produces a truthy value by itself.

A couple notes

  • Lines continuing onto the next line cannot have trailing comments or tmux will give an "unknown command" error message.
  • The "command if false" can be omitted.
  • Multiple commands for either true or false can be combined using ;
  • The command is run on the underlying shell using /bin/sh -c. Other approaches that use [[ or other non-POSIX syntax are not guaranteed to work.

EDIT: A previous version of this answer used [[, which doesn't work on systems that don't use bash. Replacing with [ solves this.