Tmux command to run shell command on active pane? - tmux

I understand that run-shell command runs the code in a "global" session which isn't necessarily linked to any active window, pane or session. However I would like to bind a key to run a command in the shell for the active pane.
Use case: map a key to opening vim in the current active pane (perhaps with certain parameters). Just like you would run run-shell "command command-parameters*".
Is there a way to do this in tmux?

Sending commands to the current pane
If you run tmux send-keys vim Enter, that will send the literal keypresses to your current pane to spawn vim. You can then bind this to whatever key combination you like.
Caveat: Because this is sending literal key presses, the pane needs a shell running already to interpret what is sent (e.g. bash / zsh / fish).
Targeting a specific pane
You can target specific destinations with the -t flag. For example, to open vim in the current session, window 5, pane 2:
tmux send-keys -t ":5.1" vim Enter
The syntax of a target is quite flexible (see the COMMANDS section of the manpage, for target-session and target-window), and this is just one example:
session-name:window-id.pane-id
When session-name is empty (as in the above example), the current session is used. The 5 identifies which window you're referring to. The .1 specifies the second pane in window #5 (counting up from 0).
$TMUX_PANE
Each pane in tmux has its own unique ID (and it is unique across all sessions). It's exposed via the $TMUX_PANE environment variable. This can also be used as a target for various tmux commands, e.g. tmux rename-window -t $TMUX_PANE $new_name.

Related

Having an interactive menu when running a shell command

I have this line in my .tmux.conf file.
bind-key q run "fish --interactive -c q"
The code for function q is written in fish. This is the code.
function q
set session (t ls | fzf)
set chosen (string split ":" $session)
t switch -t $chosen[1]
end
What q does is simple. Pipe the output from the output for t ls, which is tmux ls, into fzf. Then split the string by : and then switch tmux sessions.
When I run q normally as a command in fish, it works fine. The fzf ui shows up and I can switch sessions.
But when I use the keybinding I set. It lets me change sessions but an interactive window does not appear. It just switches sessions without letting me choose.
What I want is to be able to have an interactive menu when I use prefix-q. So that I can choose which session I want to switch to.
When tmux invokes your bind-key shell command, it does so without any tmux pane active. That is, it runs the shell detached from any tty, because a tmux command might switch panes, or create a new pane, or destroy one; so it runs in a void.
The right idea is use tmux bindings to control tmux, and shell bindings to run commands in the current shell instance, which may in turn talk to tmux. Try this (as a shell command, not in tmux.conf):
bind \cTb q
This is a shell binding. Now 'control-T' followed by 'b' will run the menu in whatever pane is active.
The problem is the command capture alters the stdin/stdout/stderr file descriptors that fzf is handed. Try forcing it to interact with the tty:
set session (t ls | fzf 2>/dev/tty)

How do you switch from a tmux pane that you've scrolled up in to another tmux pane?

If I have two tmux panes open and I go to one and scroll back to the history using CTRL-B [, then for some reason I can't switch to the other pane without making the current pane scroll back to the bottom. Does anyone know how to work around this? Note that I use some custom tmux configuration, as described here, to make it easier to navigate between vim windows and tmux panes. Is this somehow messing up how tmux scrolling works?
Is it possible you're running into a conflicting default keybinding for tmux when it's in copy mode?
When tmux is running, you can check the currently active keybindings by doing prefix+?. When I do this on a bare tmux config, I can see that C-k is bound to send-keys -X copy-end-of-line, and it will move a buffer that's in copy mode to the end of the buffer, like you're describing. Alternatively, if you're using tmux's copy-mode-vi, then it looks like C-j is bound by default to send-keys -X copy-selection-and-cancel.
Try adding the following to your .tmux.conf:
unbind-key -Tcopy-mode C-k
unbind-key -Tcopy-mode-vi C-j
After this configuration update, I'm able to use C-j and C-k to navigate panes like described in the article you linked.

Is it possible to quit tmux, while "keeping" the window in the terminal

Use case
I run command over a bastion in SSH and since the connection can be dropped we are using tmux or screen.
For short commands I'm using send-keys, let's say I'm using a command like kubectl get pods. I'd like to keep the output of this command but terminate the tmux session.
Using remain-on-exit is not what I'm looking for as it keeps a dead window.
I've seen those question already
https://unix.stackexchange.com/questions/17116/prevent-pane-window-from-closing-when-command-completes-tmux
TMUX: how to make new window stay when start shell-command quits?
I'd like to keep the output of this command but terminate the tmux session.
I use this in my ~/.tmux.conf, and now when I exit my running shell, pane output is saved to unique log file:
set -g remain-on-exit
set-hook pane-died 'capture-pane -S - -E - ; save-buffer "$HOME/logs/tmux/tmux-saved.#{host_short}-#{session_id}:#{window_id}:#{pane_id}-#{pane_pid}-#{client_activity}.log"; delete-buffer; kill-pane'
I'm not sure it's exactly what you are looking for, but you could use the capture-pane command to save the contents of a pane (subject to what is still in the pane's history) to a file.

Different starting directory per window?

I daily use tmux (2.5) on my laptop to work, and my tmux sessions have a starting directory which is the working directory I started the tmux session from. Every pane/window I open start with this starting directory as working directory.
I can change this starting directory, and this change would apply to the whole session.
But if I want to work on a different project with several panes, I could start a new window, but every pane I would open in it would start with the session's starting directory : I would have to cd to the new location for each pane which isn't practical.
If I need to work on several project/directories simultaneously, I can start a new terminal session, then cd to the relevant directory/project and start a new tmux session. That's not complicated.
But if I want to do the same thing on a server through ssh, I'd need to either :
open a new ssh session.
either embed my remote tmux sessions in an other tmux session.
Neither sounds practical to me, I'd prefer a single tmux session on the remote machine.
I think it would be more convenient to being able to start new window with its own starting directory location that would apply to any new pane opened in it. Is there a way to achieve this?
Edit :
I already tried the -c parameter of tmux new-window command.
But it doesn't assign its starting directory to the window created this way, it only applies this custom starting directory to the first pane created.
Any new pane opened in this window then uses the session's starting directory as default working dir (and not the path passed to tmux new-window).
This question is very similar to: https://unix.stackexchange.com/questions/12032/create-new-window-with-current-directory-in-tmux
It depends on your tmux version but the -c parameter does do the trick but it does not remember the setting. There used to be a default-path setting but that has been removed in version 1.9 unfortunately.
For newer versions you will need to pass along the -c in all cases (you can use an alias if you manually execute that command) or if you use key bindings you need to rebind the split/new window keys.
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
To use a custom path instead of the current pane path, execute this command:
tmux setenv custom_path /home/whatever/some/path
Put this in your config:
bind '"' split-window -c "#{custom_path}"
bind % split-window -h -c "#{custom_path}"
bind c new-window -c "#{custom_path}"
Yes, it turns out the -c option to the new-window command is what you are looking for: https://unix.stackexchange.com/questions/12032/create-new-window-with-current-directory-in-tmux Also, this: https://unix.stackexchange.com/questions/101949/new-tmux-panes-go-to-the-same-directory-as-the-current-pane-new-tmux-windows-go
So either of tmux new-window -c $(pwd) or tmux new-window -c /path/to/dir inside your tmux session should do it.

How do I disconnect all other users in tmux?

I've got a tmux session where the window is too small because some user somewhere is connected.
How do I tell tmux to disconnect all connected users?
You can use <prefix> D (where prefix is C-b by default), to chose which clients to detach; it will also list they col/lines as well as the last used time. Note the uppercase D, i.e. Shift+d.
You could also use tmux's detach-client option
detach-client [-P] [-a] [-s target-session] [-t target-client]
(alias: detach)
Detach the current client if bound to a key, the client specified
with -t, or all clients currently attached to the session speci-
fied by -s. The -a option kills all but the client given with
-t. If -P is given, send SIGHUP to the parent process of the
client, typically causing it to exit.
either from <prefix>:followed by detach [options] or on the command line inside tmux with tmux detach [options]
tmux a -dt <session-name>
a=attach
d=detach other clients (so only you can attach to this session)
t=target
I'll break out #PEdroArthur's great comment into a separate answer since it directly answers the most common use case: "being within a tmux session and wanting to disconnect all other sessions"
Run this from within your tmux session:
tmux detach-client -a

Resources