tmux, split-window and pwd

For the last couple of weeks I’ve been playing around with tmux as kind of an alternative/addon/… to iTerm2 and my terminal-focused work-style in general. One things that drove me nuts again and again is that – for some reason – I could never get the split-window command to respect my current working directory. I tried the first two solutions mentioned on the ArchLinux-wiki but the second simply did clash too much with how I’m using split-window and the first solution didn’t work at all (for some reason PWD didn’t propagate correctly and so default-path always defaulted back to the cwd of tmux’ parent shell).

With all other options gone I started to look into the third solution. This one involved extracting the PWD from the pane’s shell from the outside via /proc. But since I’m working on OSX, this doesn’t work quite like this.


The main differences here are that I can’t use /proc to look for the PWD but have to rely on lsof and that ps doesn’t have a forest-rendering. So the resulting script looks like this:


#!/usr/bin/env ruby
=begin
Helper script for tmux' split-window command that retains the original PWD
This script is based on https://wiki.archlinux.org/index.php/Tmux#.2Fproc_method
=end
SHELL = 'reattach-to-user-namespace -l zsh'


session_id, window_id, pane_id = `tmux display-message -p "#S:#I:#P"`.chomp.split(/:/)
tty = nil
in_sessions = false
in_right_session = false
in_right_window = false
in_right_pane = false

# First step is to find our tty
`tmux server-info`.each_line do |line|
    break if line.start_with? 'Terminals'
    if line.start_with? 'Sessions: '
        in_sessions = true
        next
    end
    if in_sessions and line =~ /^\s\d+: #{session_id}: \d+ window..*[flags=.*]/
        in_right_session = true
        next
    end   
    if in_right_session and line =~ /^\s{3}#{window_id}:/
        in_right_window = true
        next
    end
    if in_right_window and line =~ /^\s*#{pane_id}: \/dev\/(\S+) /
        tty = $1
        break
    end
end

raise "No matching tty found" if tty.nil?

# Now let's find the process with this tty
pid = `ps -t #{tty}`.split($/)[1].split(' ')[0]

# ... and now its PWD
pwd = `lsof -a -d cwd -p #{pid} -F n`.split($/)[1][1..-1]
args = ARGV * " "

# Let's put it all together by calling split-window with the shell and the path
`tmux split-window #{args} 'cd #{pwd}; #{SHELL}'`
Gist Thankfully, getting access to a process' PWD is pretty straight forward with `lsof -d cwd` and `ps` has a nice shortcut for filtering for a specific tty. And yes, I know that the tty-scanning is really rocky up there and this is probably the one part of this script that will evolve over time. The solution for the missing "--forest" support is also not perfect yet, but so far it works just fine for me :-) So, once you've put this script somewhere in your path (for me that ~/.local/bin/split-in-cwd.rb) and made it executable, all that is left to do, is to add it to your split-window bindings:
unbind s
bind s run '~/.local/bin/split-in-cwd.rb -v'
unbind v
bind v run '~/.local/bin/split-in-cwd.rb -h'
comments powered by Disqus