1

My setup: Tmux locally, with many connections to different servers in different panes

What I want to do: Press a shortcut within one pane, to open a new pane with the same connection already set up.

Why: workflow. a lot of times when i'm working on my servers, i need to open a new pane to do something while some other thing is running. i would save a lot of time and effort if i could just open a new pane with everything already set up.

i'm open to other ways to achieve what i want to do. i could set up tmux on the remote machines, but i prefer running tmux locally.

edit:
I have come over ssh multiplexing, which solves one part of my question (only have to log in once per server). I have added this to my ~/.ssh/config file:

### Multiplexing ###
Host *
    ControlMaster auto
    ControlPath ~/.ssh/sessions/%C
    ControlPersist 1h

Now i have to figure out how to set up a shortcut in tmux to connect to the same host in a new pane.

1 Answer 1

1

Solution

Save the following script as _tmux_duplicate_pane in a directory in your PATH:

#!/bin/bash
set -e

pid="$(tmux display-message -p '#{pane_pid}')"
pid="$(ps -o tpgid:1= -p "$pid")"
dir="$(readlink -f "/proc/$pid/cwd")"
cd -- "${dir:?}"
exe="$(readlink -f "/proc/$pid/exe")"
readarray -d '' args <"/proc/$pid/cmdline"
name="${args[0]}"
args=("${args[@]:1}")

tmux split-window "$@" -c "$dir" bash -c "exec -a ${name@Q} ${exe@Q} ${args[*]@Q}"

Make the script executable (chmod +x _tmux_duplicate_pane). Finally bind it to a keystroke in tmux. Example:

tmux bind-key -T prefix u run-shell _tmux_duplicate_pane

The command is designed to be invoked in a shell. In ~/.tmux.conf (permanent setting) the command should be bind-key ….

Now prefixu should work as you want.


Explanation

The script works by finding the foreground process group ID of the controlling terminal of the process associated with the current pane in tmux. It assumes the leader of the foreground process group ID is the command you want to duplicate. It retrieves information from /proc and invokes tmux split-window accordingly.


Notes and caveats

  • Do not run _tmux_duplicate_pane as a command in a pane. If you do this then the script will duplicate itself in a new pane, it will become a fork bomb spawning in a linear fashion. Bind to a key as shown and use it this way.

  • The script assumes the command you're after is the leader of the foreground process group in the active pane. It won't work as you expect if:

    • your (local) shell in the pane does not run commands in separate process groups (e.g. the shell is Bash with disabled job control);
    • or the command you're after is in a pipeline; e.g. interactive Bash (with job control enabled) runs the entire pipeline in one process group and the leader is the first command in the line; our script tries to duplicate the leader, but in case of a pipeline the leader may be no longer there, it may have exited early; duplicating the entire pipeline is a non-trivial task, shell-dependent, the script does not even try;
    • or the command you're after is not the leader of the foreground process group for whatever other reason.

    The script should succeed in duplicating a simple command run in an interactive modern shell (like Bash or Zsh) in the current pane.

  • Entries from /proc/$pid/cmdline are not enough to duplicate a command. The 0th entry is arbitrary (e.g. see help exec in Bash, note -a). On the other hand /proc/$pid/exe and remaining arguments from /proc/$pid/cmdline are also not enough, the same executable may act differently, depending on the 0th argument. The script uses exec -a of Bash and builds a shell command that preserves the executable and the 0th argument.

  • In some cases /proc/$pid/cmdline and /proc/$pid/exe may be wrong for our purpose. Example scenarios:

    • The process to duplicate may have changed its cmdline. The original array of arguments may be lost.
    • The process you invoked and hope to duplicate may have execed to another executable with another array of arguments. You may be able to duplicate the latter, but if the former is needed to set things up for the latter then the duplicate instance may fail.

    This shouldn't happen in case of ssh though.

  • The script will try to duplicate any command, not only ssh. You can add some logic, test $exe and/or ${args[0]}, make the script a no-op unless the command is ssh ….

  • When the duplicate command exits, there will be no shell in its pane. The pane will terminate unless the remain-on-exit option is on in tmux. A simple workaround is to force an interactive shell after the command. E.g. instead of tmux split-window … "…" you do tmux split-window … "…; bash".

  • While duplicating a command, the script tries to do this in the current working directory of the original process. In some cases this will be wrong, especially if the process takes relative paths as arguments and then changes its working directory. The script cannot know which directory was current at the time the command was invoked; it knows the now-current directory which may or may not be the same.

  • The script does not try to replicate the entire environment of the original process. If you run SSH_ASKPASS=… ssh … in a pane and try to duplicate this then the new ssh command will not know the variable. Parsing /proc/$pid/environment is possible, I guess; the script does not do this though.

  • The script retrieves different information at different moments. This means it's prone to race conditions.

  • The purpose of cd in the script is to make the script fail early in case of trouble.

  • Arguments you provide to the script itself will be passed to tmux split-window. This means you can bind tmux bind-key … '_tmux_duplicate_pane -h' and enjoy horizontal splits.

1
  • wow. this is amazing! thank you so much! i put the executable file in /home/user/bin/, added the bind-key ...-argument to my .tmux.conf-file, reloaded my terminal and tested it out locally and on different servers. It worked right away just the way i wanted it to! Sep 2, 2022 at 11:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .