mrt181
mrt181

Reputation: 5316

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

Upvotes: 66

Views: 43351

Answers (16)

jdloft
jdloft

Reputation: 97

Tmux's if-shell can be used to check the tmux version.

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

checks whether or not the tmux version is greater than or equal to 2.1. Using this we can set your mouse commands depending on the tmux version.

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'

And set it for later versions of tmux:

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

Upvotes: 7

weichao
weichao

Reputation: 3421

If you want to check split-window in different tmux version to solve https://github.com/christoomey/dotfiles/issues/127

try

run-shell 'tmux setenv -g TMUX_VERSION $(tmux -V | \
                           sed -En "s/^tmux[^0-9]*([.0-9]+).*/\1/p")'

# split panes using \ and -
# https://github.com/christoomey/dotfiles/issues/127
if-shell -b '[ "$(echo "$TMUX_VERSION >= 3.0" | bc)" = 1 ]' \
    "bind \\\\ split-window -h -c '#{pane_current_path}'; \
"

if-shell -b '[ "$(echo "$TMUX_VERSION < 3.0" | bc)" = 1 ]' \
    "bind \\ split-window -h -c '#{pane_current_path}'; \
"

if-shell will re-escape \, so you need \\\\

reason: https://github.com/tmux/tmux/issues/1827#issuecomment-508985729

Upvotes: 0

Micah Smith
Micah Smith

Reputation: 4471

This is kind of a hassle. 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.

Upvotes: 25

thiagowfx
thiagowfx

Reputation: 5770

Another option is to use set -gq (c.f. FAQ) to set options silently. If they are not available in older versions of tmux, they will just be silently ignored.

For example, this is one way to set the newly added allow-passthrough option, without erroring out in tmux versions prior to 3.3a.

Upvotes: 0

Andy
Andy

Reputation: 3215

TL;DR some key improvements can:

  • Keep good sh and tmux compatibility back to at least 1.8
  • Free you from the bc requirement for portability
  • Increase readability of the if statements
  • Let you use {} on larger complicated nested string blocks, provided those are for 3.0 and newer

Version check

I prefer awk and sed (core linux tools) approach to getting the version:

run-shell 'tmux setenv -g TMUX_VERSION $(\
             tmux -V | \
             sed -En "s/^tmux[^0-9]*([.0-9]+).*/\1/p" | \
             awk "{major=int(\$1); \
                   minor=\$1-major; \
                   print int(major*100+minor*10) }")'

This is very similar to solution that use string manipulation techniques, except I'm able to easily do math in awk.

E.g.

  • tmux 1.3 becomes 103
  • tmux 3.2a becomes 302

Now instead of having to remember the bc/other expression every time you want to if-shell, it's a fairly normal sh expression.

'[ "${TMUX_VERSION}" -lt "200" ]' # Version < 2.0
'[ "${TMUX_VERSION}" -le "215" ]' # Version <= 2.15 (a pretend version 2.15)

Note Some ancient versions of GNU sed don't like -E flag and need -r instead. (BSD (darwin) doesn't support -r). These are all EOL and should no longer be a problem. But if that level of portability matters, an addition run-shell test could resolve that distinction.

If-shells examples

if-shell -b '[ "${TMUX_VERSION}" -lt "200" ]' " \
             setw -g mode-mouse on; \
             set -g mouse-select-window on; \
             set -g mouse-select-pane on; \
             set -g mouse-resize-pane on; \
             set -g mouse-utf on"

if-shell -b '[ "${TMUX_VERSION}" -ge "200" ]' " \
             set-window-option -g mouse on"

{} strings

I could not get @Tom Hale solution using {} to work. This appears to be a tmux 3.0 feature. Just having } on a line by itself will upset tmux 1.8 - 2.9, so I can't figure out how to use it.

What I found works, is if I used ' as the outer most string quotes, then I can use {} internally, if and only if I escape it so that it is interpreted as a single line (although it will be readable as multiple lines for me)

To keep older versions of tmux happy:

  • { and } must come at the end of the line, no } on their own
  • Every line must end in \. The essentially means it's a single line, partially defeating the key multiline benefit of {}. However this still improves readability and allows easier string nesting.
  • Since every line must end in \, multiple command will still need ; between them when on multiple lines (See copy-mode below)

Rehash This parses successfully on 1.8 (I never test older) and newer, as long as the tmux version check is for versions 3.0 or greater. The examples above target versions 1 and 2, so cannot use the escaped {} method

Snippet of a complicated example

if-shell -b '[ "${TMUX_VERSION}" -ge "300" ]' ' \
  bind-key -T root MouseDown3Pane \
    if-shell -F -t = "#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}" \
      "select-pane -t= ; send -M" { \
        display-menu -t= -xM -yM -T \
          "#[align=centre]#{pane_index} (#{pane_id})" \
          "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}" < "send -X history-top" \
          "#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}" > "send -X history-bottom" \
          "" \
          "#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}" C-r { \
            if -F "#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}" \
              "copy-mode -t=" ; \
            send -Xt= search-backward "#{q:mouse_word}" \
          } \
          "#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}" C-y { \
            copy-mode -q ; \
            send-keys -l -- "#{q:mouse_word}" \
          } \
        ... } \
'

Life hack: The outer '' can be replaced with {} while you use your editor of choice for syntax highlighting, but you'll have to remember to put it back

Upvotes: 3

Tom Hale
Tom Hale

Reputation: 47013

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.

Upvotes: 75

ramon
ramon

Reputation: 900

Newer versions of tmux support neater if conditions. The below works on tmux version 3.2a:

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

Upvotes: 2

Good Pen
Good Pen

Reputation: 829

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'

Upvotes: -1

Green Bird
Green Bird

Reputation: 199

I have this config for many years.

  • Support versions like tmux 2.4 tmux 2.4a tmux next-3.3
  • No bc command
  • Support Cygwin, macOS and 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'

Upvotes: 2

cdleonard
cdleonard

Reputation: 7060

I recently ran into this issue and my solution was to write an "tmux-older-than" script:

#! /bin/bash

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

This makes tmux.conf much more readable:

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

Upvotes: 4

Matthew Strasiotto
Matthew Strasiotto

Reputation: 369

This is not an alternative, but rather an extension to the accepted answers - (Tom Hale's answer is the most robust & complete).

For less repetition in your .tmux.conf, you can store your shell test strings in tmux env vars (available since 0.8 in 2008) & interpolate them at run-time for 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

Upvotes: 0

silico-biomancer
silico-biomancer

Reputation: 300

Current latest release is 2.9a, which throws off many of the direct comparisons used here.

My alternative makes use of sort -V, which is much more robust for handling version comparisons.

Edit: A commenter points out that sort -V is not available in BSD sort, for example on native OSX. However this is still an answer that accounts for versions that aren't purely numerical.

# 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"

Upvotes: 1

e-pirate
e-pirate

Reputation: 193

I have combined suggested solutions to a working one (tested on tmux 1.8 and 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"

Upvotes: 0

Ingo Karkat
Ingo Karkat

Reputation: 172758

I also stumbled over configuration mismatches in different tmux versions. After reviewing all the solutions here and in this related question on SuperUser, I've implemented the following variant:

# 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"

With this, version-specific configuration can be put in (multiple) configuration snippets for a particular version. This is similar to the solution of @VincentHsu, but:

  • It does not require an external shell script.
  • It does not segregate into fixed version ranges (... / 1.9 to 2.0 / 2.1 ...). Instead, one can use (sym)links to share a configuration snippet among multiple tmux versions.
  • It does not hardcode a single filename for a version. By allowing multiple configuration snippets for each version, parts can be shared among versions while others are kept version-specific. This should offer the utmost flexibility.
  • There's just a single configuration element in the original ~/.tmux.conf. Other solutions like the one from @TomHale duplicate the version test for each configuration element.

Upvotes: 21

bdellaterra
bdellaterra

Reputation: 111

On some machines I was getting a false-positive result with the double bracket ('[[') syntax. So I came up with an alternative using 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 < \"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 >= \"2.1\")}'" \
    "set -g mouse on ;"

Upvotes: 8

Vincent Hsu
Vincent Hsu

Reputation: 291

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

Upvotes: 29

Related Questions