Skip to content
This repository

Way to ignore this script if not on OS X? #8

Closed
tylerball opened this Issue February 21, 2012 · 26 comments
Tyler Ball

I like to keep my ~/.tmux.conf consistent across OS X and Linux. Currently the script is not present on my Linux machines and so tmux crashes if the reattach-to-user-namespace command is added. Are you aware of any solution to this problem?

Chris Johnsen

Here are two alternatives that should work:

Conditional Configuration

Use the if-shell command of tmux to conditionally include the OS X–specific bits of configuration.

Update: There is a race condition inherent in using if-shell to make configuration changes. See my later comment.

Update (much later): The race condition that I described was fixed in tmux 1.9, so this kind of conditional configuration works fine there.

In a new ~/.tmux-osx.conf file:

set-option -g default-command "reattach-to-user-namespace -l zsh"

Use whatever bit of configuration you have been using on OS X: include the path to the wrapper if it is not normally in your PATH, and/or specify a different (path to the) shell.

Then, in your main ~/.tmux.conf file, remove the references to reattach-to-user-namespace (i.e. the bits of configuration moved to the new file described above) and include this line instead:

if-shell 'test "$(uname)" = "Darwin"' 'source ~/.tmux-osx.conf'

Actually, if you can arrange to not have the ~/.tmux-osx.conf file present at all on your non–OS X systems, then you can probably get away without the if-shell, just use source ~/.tmux-osx.conf; tmux seems to ignore missing files (though this could reasonably change in future versions of tmux).

Provide reattach-to-user-namespace as a Script

Drop a Bash script named reattach-to-user-namespace into a directory in the PATH on your non–OS X systems:

#!/bin/bash
# For non-OS X systems, a placeholder for the program from
# https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard
exec "$@"

(Be sure to chmod +x the file.)

Tyler Ball

Thanks a lot, the first option worked perfect for me.

Tyler Ball tylerball closed this February 22, 2012
Chris Johnsen

I have noticed that there is a race condition when using if-shell to make configuration changes. tmux runs the shell command (and the resulting tmux command) “in the background”. This means that the rest of the startup process proceeds while the platform check (et cetera) happens.

It is entirely possible that tmux will create the first pane (possibly several panes/windows/sessions if you use split-window/new-window/new-session in your .tmux.conf) before the conditional is evaluated and/or before the default-command option is assigned its desired value.

Workarounds

As described in my earlier reply, you could use the unconditional source-file ~/.tmux-osx.conf technique (where ~/.tmux-osx.conf is missing on non–OS X systems) or arrange for the “dummy” wrapper on non–OS X systems. Unfortunately, both of these alternatives prevent you from keeping a full set of identical sets of files in your home directory on both kinds of systems (assuming you put the dummy wrapper in ~/bin or similar).

Here is yet another method:

  1. Use this ~/.tmux-osx.conf:

    source-file ~/.tmux.conf
    set-option -g default-command "reattach-to-user-namespace -l zsh"
    
  2. Eliminate any mentions of ~/.tmux-osx.conf or reattach-to-user-namespace from your ~/.tmux.conf.

  3. In a directory early in your PATH (e.g. $HOME/bin) make a tmux script:

    #!/bin/sh
    unset cfg
    test "$(uname)" = "Darwin" && cfg=$HOME/.tmux-osx.conf
    exec /path/to/actual/tmux ${cfg+-f "$cfg"} "$@"
    

Instead of the last step, you could arrange to conditionally define an alias for your shell that does something similar. Personally, I like the script better since it will work correctly from any process that does an exec(2)-family syscall as long as you have preconfigured the PATH environment variable.

Tyler Ball

I don't have tmux set up a default set of panes or anything on startup, I use teamocil for that, so I think I can stick with that solution for now. Having different files on different OSes makes it hard to keep a consistent set of dotfiles.

Creating a simple bash/zsh alias does indeed work.

if [[ "$(uname)" = "Darwin" ]]; then
    alias tmux='tmux -2 -f ~/.tmux-osx.conf'
else
    alias tmux='tmux -2'
fi

But yeah, that script is nicer.

Chris Johnsen

Just one clarification: The conditional-configuration-via-if-shell race condition can affect the initial window even if you are not creating multiple panes/windows/sessions in your ~/.tmux.conf.

The “possibly several panes/windows/sessions” parenthetical in my previous comment was meant to indicate that more than just the initial window could be affected, not to imply that you would only be affected if you were creating extra panes/windows in the configuration file.

I have not used teamocil, but (after a cursory look at the code) it seems like you could end up reusing the initial window if you use the --here option, or if you later switch back to the initial window after loading your teamocil-based window/pane arrangement.

teamocil is probably slow enough to start that the proposed if-shell-based conditional configuration should be in place by the time it creates any new panes/windows. However, due to the race condition, the shell in the initial window (whether reused via --here or left in place and later manually reused) may not always have been started under the reattach-to-user-namespace wrapper.

Anyway, this all applies only to the if-shell-based conditional configuration. If you use the previously described tmux wrapper script (or your alias variation) you will not be subject to this race condition that could affect “early” panes/windows.


tmux does an implicit new-session if you run tmux without any command arguments when starting the server.

Not that I expect teamocil to be particularly slow, just that starting its Ruby instance and evaluating its code is probably much slower than starting a shell, evaluating the condition, and tmux finishing its source-file command.

Anthony DiSanti

I sync my tmux.conf down via a dot files repo and then symlink it into my home directory, so I was looking for an option that would be portable but wouldn't require any manual setup on non-osx machines. For that reason, I opted for your "Provide reattach-to-user-namespace as a Script" option. However, I use the script on all systems and embed the logic for determining the system there. I create another script in my path (in this case a directory that comes down with my dot files repo) called reattach-to-user-namespace-tmux:

#!/usr/bin/env bash

if [[ "$(uname)" = "Darwin" ]]; then
  reattach-to-user-namespace $@
else
  exec "$@"
fi

And then I change the command in my tmux.conf to:

set-option -g default-command 'reattach-to-user-namespace-tmux -l "$SHELL"'

Works like a charm! Thanks for all the time you put into addressing this issue.

Simon Szustkowski

What about copying text out of tmux? As the internet considers, i have a key binding called bind y run "tmux save-buffer - | reattach-to-user-namespace pbcopy" What about keeping this .tmux.conf synchronized between my linux and mac os machines?

Anthony DiSanti

I use the same binding for copy. What I've been meaning to get around to is creating a pbcopy script that will dispatch to the remote clipboard utility or just silently fail on systems where that utility doesn't exist.

Currently, I just deal with not having tmux's copy functionality on non-osx machines. As a workaround, I use the local system clipboard (in iTerm2 hold option, then click and drag to do a standard selection then hit command+c to copy to your local box's clipboard).

Simon Szustkowski

Yes, this would be the deal for me too. But this will fail when i want to copy text which extends out of my iTerm window and i have to use the tmux copy mode. :(

Anthony DiSanti

Yeah, it's not a good solution. I'm assuming you're talking about when you have multiple panes in a single window (like vim's windows in a tab). You can break-pane for a minute and then join-pane it back in. If it's a common enough procedure for you, you could implement these "maximize a pane" bindings (in the second answer):

http://superuser.com/questions/238702/maximizing-a-pane-in-tmux

I haven't done this myself, so ymmv

Chris Johnsen

@simonszu, below is an example of a script similar to the one that @AnthonyDiSanti described (though someone else will need to fill in the non–OS X bits). If you can deploy it to all your platforms under the same name, then you can use it in your binding and have an identical .tmux.conf (and script) on all your systems.

For example:

#!/bin/sh

# tmux-osdep-copy-buffer
#  Use an OS-dependent method to place the contents of the current
#  tmux paste buffer into the "system clipboard".

#exec 2>&1 # helpful for capturing stderr while debugging

n=${0##*/}

die() {
    # If this was run via run-shell, stdout will be captured and
    # displayed in a temporary copy-mode in the current pane.
    printf "$n"': %s\n' "$*"
    exit 1
}

case "$(uname -s)" in
Darwin)
    # If reattach-to-user-namespace is not in your PATH, you may
    # set the RATUNS environment variable (e.g. before your start
    # your tmux server, or in your .tmux.conf file).
    tmux save-buffer - | "${RATUNS:-reattach-to-user-namespace}" pbcopy ||
        die 'unable to use pbcopy'
    ;;
#Linux|*BSD|SunOS)
#    # Check for DISPLAY and xsel/xclip, and use them (or whatever is appropriate).
#    ;;
*) 
    die 'unknown platform'
    ;;
esac
jeff peterson

A fairly old issue, but I stumbled across it in a Google search.
How about:

set-option -g default-command "reattach-to-user-namespace -l zsh 2> /dev/null || zsh"
Mario Ricalde

@jeffpeterson excellent! Thanks for sharing this!

Anthony DiSanti

@jeffpeterson That's a very direct solution, thank you. My only concern is that waiting for a command to fail every time you open a new pane or window would slow down your general use of tmux. Have you found that to be an issue?

jeff peterson

@AnthonyDiSanti It shouldn't take any longer to fail than it does to run.

Ernesto García
gnapse commented June 17, 2013

@jeffpeterson your suggestion seemed great at first, but I've been noticing recently that many commands in my shell have been omitting some of their output.

Today I decided to find out what the reason was, and after noticing that this only happened while inside tmux, it took me little time to detect that your suggestion has the culprit. Is it just me? For instance, I get no output from wget, curl, git clone, git push, git pull. They work fine, but I get no feedback on the shell as to how they did. I'm also not getting any errors when I invoke invalid commands.

After I rolled back that change, they all started to give me the usual output. Too bad :( because this was the ideal solution!

jeff peterson

@gnapse Yeah, I noticed the same thing soon after posting it. The missing output is because standard error is redirected to /dev/null. You can use:

set-option -g default-command "reattach-to-user-namespace -l zsh || zsh"

But, if the first zsh session doesn't exit with exit code 0, it will run the second zsh command.

Jim Myhrberg

@jeffpeterson The issue with not directing STDERR to /dev/null is that you get command missing error on each new window/pane created when reattach-to-user-namespace is not available.

Personally I opted for a slight variation of @AnthonyDiSanti's solution by using a proxy-command. But instead of checking for the OS type, I simply check if the reattach-to-user-namespace command is available.

Details are here: jimeh/dotfiles@3838db8

gkb
gkb commented August 30, 2013

I'm using a script based on what @ChrisJohnsen provided earlier. It assumes that the xclip utility is present on Linux.

#!/bin/sh

#  Use an OS-dependent method to place the contents of the current
#  tmux paste buffer into the "system clipboard".

#exec 2>&1 # helpful for capturing stderr while debugging

n=${0##*/}

die() {
        # If this was run via run-shell, stdout will be captured and
        # displayed in a temporary copy-mode in the current pane.
        printf "$n"': %s\n' "$*"
        exit 1
}

case "$(uname -s)" in
Darwin)
        # If reattach-to-user-namespace is not in your PATH, you may
        # set the RATUNS environment variable (e.g. before your start
        # your tmux server, or in your .tmux.conf file).
        case "$1" in
        copy)
                tmux save-buffer - | "${RATUNS:-reattach-to-user-namespace}" pbcopy ||
                        die 'unable to use pbcopy'
                ;;
        paste)
                tmux set-buffer $(reattach-to-user-namespace pbpaste)
        tmux paste-buffer
                ;;
        *)
                die 'unknown command: must be copy or paste'
                ;;
        esac
        ;;
Linux|*BSD|SunOS)
        # Check for DISPLAY and xsel/xclip, and use them (or whatever is appropriate).
        case "$1" in
        copy)
                tmux save-buffer - | xclip -i -sel clipboard
                ;;
        paste)
                tmux set-buffer "$(xclip -o -sel clipboard)"; tmux paste-buffer
                ;;
        *)
                die 'unknown command: must be copy or paste'
        esac
        ;;
*)
        die 'unknown platform'
        ;;
esac

In my .tmux.conf file, I've included these lines.

bind C-c run "tmux-osdep-copy-buffer copy"
bind C-v run "tmux-osdep-copy-buffer paste"

This works really well for me.

John Axel Eriksson

I just do this:

set-option -g default-command "which reattach-to-user-namespace >/dev/null && reattach-to-user-namespace -l bash || bash"
Anthony DiSanti

@johnae That's a great solution as long as your interactive sessions always exit with code 0. I'm not sure why an interactive session would ever exit with anything other than 0 unless done intentionally, so I think that's perfect.

Behrang Saeedzadeh

It should be possible to use if-shell now: http://sourceforge.net/p/tmux/tickets/65/

Chris Johnsen
Owner

Yes, the if-shell configuration that I originally described in my first comment on this issue now works race-free in tmux 1.9:

In .tmux.conf:

if-shell 'test "$(uname)" = "Darwin"' 'source ~/.tmux-osx.conf'

In .tmux-osx.conf:

set-option -g default-command "reattach-to-user-namespace -l zsh"
Jim Myhrberg
jimeh commented March 10, 2014

@ChrisJohnsen I still prefer the solution I already have in place: jimeh/dotfiles@3838db8

No extra OSX-specific config files, and backwards compatible with Tmux 1.8. It does require that you distribute your .tmux.conf along with the wrapper command though, but obviously I keep all my dotfiles in a git repo, so not really an issue me at least :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.