How to write if statement in .tmux.conf to set different options for different tmux versions?
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.
Solution 4
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.
Solution 5
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 ;"
Related videos on Youtube
Comments
-
mrt181 over 2 years
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 other2.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
-
Etan Reisner about 8 yearsWhy do you have both
[[ ]]
and(( ))
in that test? I would think just(( ))
would be enough assumingif
(which isif-shell
?) tests the return code. -
mrt181 about 8 yearsfixing the if statement to
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"
fixes the problem, Can you post your comment as an answer. -
Etan Reisner about 8 yearsActually
(( ))
can't handle the decimal numbers so if that works it is a different reason (or accidental). Does tmux have a way to test for features? (I assume you only want that turned on in versions that support it.) -
mrt181 about 8 yearsI do not know if tmux can test for features, I am rather new to tmux.
-
Ciro Santilli OurBigBook.com almost 8 yearsos detect: superuser.com/questions/539595/…
-
-
Jongwook Choi about 8 yearsThe
if-shell
configuration for tmux <2.1 seems not working. Even with tmux 2.0, the bash shell says true forif [[ $(tmux -V | cut -d' ' -f2) -lt 2.1 ]]; then echo 'true'; true; else echo 'false'; false; fi
, but withif-shell "if [[ $(tmux -V | cut -d' ' -f2) -lt 2.1 ]]; then true; else false; fi" "display true;" "display false;"
, tmux says thatunknown command: display 2
. It means theshell
command is evaluated as false. -
Ciro Santilli OurBigBook.com almost 8 yearsThat
bc
hack is hardcore. -
Wyntau over 7 yearsI get the same problem. if-shell always get false return. anyone coule please me?
-
Micah Smith over 7 yearsThis simply doesn't work. Just try running
[[ `tmux -V | cut -d' ' -f2` -lt 2.1 ]]
in bash. I get-bash: [[: 2.2: syntax error: invalid arithmetic operator (error token is ".2")
. The reason is that these test operators don't handle floating points, for which you needbc
. -
jdloft over 7 yearsSorry, I was testing in Z shell which allows the use of the "-lt" operator like that. Try replacing it with the '<' symbol (and the '>' symbol for greater than).
-
Shubham Chaudhary over 7 yearsA cat dies whenever somebody does
boolean ? true : false
instead ofboolean
only -
Carl Patenaude Poulin over 7 yearsI'm wondering if there's a way to make this work with nested if statements.
-
Tom Hale over 7 yearsI'm sure you could @Carl, but your quoting of
"
characters would get hairy. @pana You'll need a;
between lines 2 and 3 in the last example. Also, you havemouse
twice. See my answer for an example of a continuation within a continuation. -
Tom Hale over 7 yearsThe configuration management is already non-trivial managing 3 configuration files. The
tmux
devs introduce incompatibilities quite a bit and will probably continue to do so. I prefer a solution using only.tmux.conf
which I based on this answer. -
Vincent Hsu over 7 yearsI have tried the
if-shell
solution, but I can't find a way to detect the version numbers correctly on my 3 machines (Ubuntu 14.04 (tmux 1.8), Ubuntu 16.04 (tmux 2.1) and macOS Sierra (tmux 2.3)). So I choose to call bash directly. I would be happy if someone make "only one .tmux.conf" work. -
joelparkerhenderson about 7 yearsThanks Tom, this is great. I've added a thank you to you on my repo github.com/sixarm/sixarm_tmux_dotfiles
-
Andrei Bârsan almost 7 yearsAs someone who uses the same dotfiles on OSX and 2-3 different linux boxes, I can't thank you enough! This answer seems to be the most versatile of them all.
-
Krisztian over 6 yearsBrilliant solution, that should be the accepted answer! Thanks (I'd add
head -1 |
aftertmux -V |
in case they will include more lines into its description later.) -
raimue about 6 years@Krisztian Should not be necessary, as the
exit
already causesawk
to stop processing on first line. -
Krisztian about 6 years@raimue, thanks, I didn't know that, it seems to work indeed:
echo -e "tmux 2.0-master\ntmux 2.8" | awk '{exit !($2 < "2.1")}' && echo ok || echo no
-
Dave almost 6 yearsHey, has anyone ever got this to work? tmux 'run-shell' is hard-coded to run /bin/sh. None of the example above work in /bin/sh.
-
William Pursell over 5 yearsMinor nit:
[
is not an operator, it is a command. This is only an important distinction because many people mistake[
as being part of the shell grammar, which it is not. -
Tom Hale over 5 years
sh
andbash
don't allow for arithmetic comparison of floats. See this answer for a solution using the bench calculatorbc
-
Tony Clifton over 5 yearsthis is pointed out below in Micah's answer, but I was having issues due to missing semicolons here before the back slashes (it was all being interpreted as a single long command). Thanks for the example!
-
Gerrit-K over 5 years@TonyClifton Indeed, not only the missing semicolons cause problems, but also everything past the first comment char (
#
) was ignored in my case ... -
pdoherty926 about 5 yearsThis is an excellent answer. I was able to achieve what I was after and I learned a few things about tmux and Bash. Thanks!
-
Tom Hale almost 5 years@Griddo thanks for the edit. I've updated based on v2.9, but as I didn't encounter the issues you experienced, I'd appreciate your review. What version are you using which baulks at the
#
and lack of;
? -
Gerrit-K almost 5 years@TomHale I'm currently using v2.6 on linux mint and also something >v2.4 as well as v2.8 on WSL debian. See this minimal example which exactly reproduces the two errors.
-
danemacmillan almost 5 yearsIt should be noted that these version checks will not work if there are letters and other characters in the release version. In the spirit of tmux's FU SEMVER, I offer up the last two releases on GitHub that defy these version checks:
2.9a
and3.0-rc3
. github.com/tmux/tmux/releases -
danemacmillan almost 5 years
$(tmux -V | cut -c -8 | cut -d' ' -f2)
-
Tom Hale almost 5 yearsThanks for the heads-up @danemacmillan. Their release management is FUBAR. I've updated with a single
sed -n
. -
danemacmillan almost 5 years@TomHale That
sed
string is much better, but you forgot to pipe it. I'm taking it, thanks! -
Tom Hale almost 5 years@danemacmillan Please feel free to use the
edit
link to improve any answer on this site! :) -
raychi almost 4 years@TomHale, the
setenv
line doesn't work for versiontmux.orig 2.8
value.sed -En "s/^tmux.* ...
does work. -
philb almost 4 yearscareful though,
sort -V
does not always work, for example it does not work on macOS 10.11 which uses BSDsort
-
silico-biomancer almost 4 yearsDrats, that's a good caveat. I usually put gnu sort on my OSX machines, so I missed that. I'll add the caveat to the answer.
-
Tom Hale almost 4 years@raychi Sorry, but I don't quite follow. Please suggest an edit.
-
raychi almost 4 years@TomHale, My
tmux -V
returns the stringtmux.orig 2.8
. This value doesn't work with the run-shell sed command. Change the sed pattern tos/^tmux.* ....
(add .* after "tmux"). Suggest edit didn't work since change needs to be 6 characters long and mine is just 2-chars :) -
Matthew Strasiotto over 3 yearsThe current head of master has the version string:
tmux next-3.3
The pattern in the answer didn't work, an (other) appropriate pattern for sed is:bash sed -En "s/^tmux[^0-9]*([.0-9]+).*/\1/p"
I read your linked issue, & I share your disappointment in the maintainers' attitude towards semver. As you pointed out, that 23 minutes of .conf fixup for one dev -> thousands of cumilative person-hours across the userbase. -
Matthew Strasiotto over 3 yearsHey, just regarding this, one way to clean-up your
tmux.conf
that implements this so it's a little more DRY: You can store your shell test strings in tmux env vars, (since 0.8 in 2008) and interpolate them @ run time forif-shell
:V33_GTE='[ "$(echo "$TMUX_VERSION >= 3.3" | bc)" = 1 ]'
V33_LT='[ "$(echo "$TMUX_VERSION < 3.3" | bc)" = 1 ]'
if "$V33_GTE" bind -N "Mouse mode off" M
-
Matthew Strasiotto over 3 yearsyour cut command isn't likely to be robust enough across different version strings. For example
tmux -V
at the HEAD of the repo istmux next-3.3
.tmux -V | cut -c 6-
givesnext-3.3
. Also as others have pointed out[[ ]]
test constructs are a bash feature that's not portable across systems, as the only guaranteed shell is the POSIX compliantsh
. -
Tom Hale over 3 years@MatthewStrasiotto Sorry, I don't follow, what repetition does this remove?
-
Matthew Strasiotto over 3 years@TomHale instead of copy-pasting & evaluating the same shell test, you wind up copy pasting an env-var that stores the test & evaluating that. The benefit is marginal really
-
wisnij over 3 yearsTo avoid having to use
bc
(which isn't installed on some of the machines I often log into remotely) I've been doing this:run-shell "tmux setenv -g TMUX_VERSION $(tmux -V | sed -E 's/^[^0-9]*//; s/[^0-9]*$//' | awk -F. '{ printf \"%d.%02d\", $1, $2 }')"
and thenif-shell 'bash -c "if [[ $TMUX_VERSION < 1.09 ]]; then :; else false; fi"' '...'
. That is, I format the version in a way that can be compared lexicographically, and use the regular bash<
and>
operators to compare them as strings. It works on tmux 1.8, 2.1 and 3.1b, but I haven't tested exhaustively. -
Elliptical view over 3 years
set -g mouse
(without on or off) toggles all by itself. Try this:bind-key c-M set-option -g mouse \; display-message 'Mouse #{?mouse,on,off}'
-
Matthew Strasiotto over 3 yearsI can change the answer to reflect that different approach, but it's not relevant to the answer, it's an example of a binding that's conditional on an
if-shell
statement that evaluates an interpolated statement. -
NeilG almost 2 yearsThat
bc
hack is so hardcore it fails for me: mytmux
version is "3.0a", @CiroSantilliПутлерКапут六四事 Can be fixed withtmux_version="$(tmux -V | cut -c 6-8)"
-
DJCrashdummy almost 2 years@NeilG that has nothing to do with the
bc
hack, just thecut
part is pretty unreliable! - also your fix will break for other outputs liketmux next-3.3
or even if just one part uses more than one digit. || although it gets a little bit longer, i usesed
instead ofcut
which works for all cases known:sed -E -e 's/[[:alpha:][:space:][:punct:]]*([[:digit:]]*\.?[[:digit:]]+).*/\1/'