Attach a tmux session over SSH and run a command - tmux

This works to attach a session with a specified name or create if it doesn't exist:
tmux new-session -A -s encode
but I need to add a command to run ie.
tmux new-session -A -s encode 'ls /home/user/'

You'll want to take a look at the tmux send-keys command. From the man
Send a key or keys to a window. Each argument key is the name of the key (such as C-a or npage ) to send; if the string is
not recognised as a key, it is sent as a series of characters. The -l flag disables key name lookup and sends the keys liter-
ally. All arguments are sent sequentially from first to last. The -R flag causes the terminal state to be reset.
In your case you can do
tmux new-session -d -A -s encode
tmux send-keys -t encode 'ls /home/users' C-m
tmux attach -t encode
C-m is the Enter key. The -d flag is to create the session, but not attach to it.

Related

How do i switch the active tmux session inside a shell script

I am trying to create an automated clean shutdown of my opensimulator servers.
On each server, I have several Tmux sessions. Inside each session, there are several windows. In the following example, there are only two sessions to keep things simple.
sara#opensim:~$ tmux ls
Robust: 5 windows (created Tue May 12 22:08:28 2020)
Simulators01: 6 windows (created Tue May 12 23:30:38 2020)
sara#opensim:~$
In the full version, there will be 10+ Simulator sessions.
What I want to do is use a shell script to select a specific session. Every session will have a closedown shell script which looks similar to this:
#!/usr/bin/env bash
SESSION="InstancesTesting"
echo "checking for session - $SESSION"
SESSIONEXISTS=$(tmux list-sessions | grep $SESSION)
if [ "$SESSIONEXISTS" != "" ]
then
echo "session found"
tmux attach-session -d -t $SESSION
tmux select-window -t '0821'
tmux send-keys -t '0821' 'quit' C-m
tmux select-window -t '0900'
tmux send-keys -t '0900' 'quit' C-m
tmux select-window -t '0901'
tmux send-keys -t '0901' 'quit' C-m
tmux select-window -t '0910'
tmux send-keys -t '0910' 'quit' C-m
tmux select-window -t '0911'
tmux send-keys -t '0911' 'quit' C-m
tmux select-window -t '0920'
tmux send-keys -t '0920' 'quit' C-m
echo "finished shut down call for $SESSION"
else
echo "session not found skipping"
fi
The problem line is
tmux attach-session -d -t $SESSION
When it is run from a shell script, everything after that stops until the session is detached. However, without attaching the session only the windows of the last attached session can be accessed.
I can't simply kill the session at the end of the quit commands because the simular running inside each window can take up to 10 mins to shut down. Neither do I want to wait 10 mins between starting each shutdown. I want to set them all going then wait for the processes to close before doing a reboot.
What I need is to either:
1. Attach a session and allow the script to keep running without pressing ctrl+b D to detach.
or
2. Change the session which is being accessed without actually attaching it like the above example.
I have also tried
tmux switch-client SessionName
tmux switch-client -t SessionName
tmux switch-client -n
All of these return the same result
no current client
I have also tried
tmux send-keys -t 'WindowName' 'tmux choose-session' C-m
tmux send-keys -t 'WindowName' '0' C-m
Unfortunately, this option also states there is no client.
I am sure this must be possible, I am going round in circles, please help
There is no concept of a "selected session", clients have an attached session but outside tmux when you don't specify a session the choice of which to use is made separately each time. See here: https://github.com/tmux/tmux/wiki/Advanced-Use#the-default-target
But you shouldn't need it. You are already using -t to specify the window, use it to specify the session as well:
tmux send-keys -t "$SESSION:8021" 'quit' C-m
You don't need select-window either unless you later plan to attach, and then one select-window at the end would do. See https://github.com/tmux/tmux/wiki/Advanced-Use#command-targets for a description of targets.
You may also find the has-session command useful instead of using grep, or the -F flag to list-sessions.

Stop tmux detached session closing when command finishes

When I fire up a new, detached tmux session using something like
tmux new-session -d -s "newname" 'python my file.py'
the tmux session seems to disappear from tmux ls once my python program has finished. I'd like to drop back into a shell. How do I do that?
Use send-keys to run your script in the default shell process in the new window.
tmux new-session -d -s "new name"
tmux send-keys -t "new name:0" "python my file.py" Enter

Tmux not detaching, sending keys from script

I have a remote server that I can reboot remotely, and I'm trying to make a script that starts my RESTful service. The script is called from .bash_profile. It starts it correctly, but it stays attached to the new tmux session, and hangs until I ssh in and manually detach it. Currently I'm sending C-b d but it doesn't detach.
Here is my small script:
tmux new-session -s cf
tmux send-keys -t cf 'cd ~/server' C-m
tmux send-keys -t cf 'sudo ./gradlew jettyRun' C-m
tmux send-keys -t cf 'C-b d'
How can I achieve this?
you can detach the session when creating it using -d :
tmux new-session -ds cf
and then you can access it anyway

How to send a command to all panes in tmux?

I like to call :clear-history on panes with a huge scrollback. However, I want to script a way to send this command to all the panes in the various windows.
I know how to send a command to all the windows, courtesy of this question, but how do I send a command to all the panes of which window as well?
send-keys and synchronize-panes from the tmux manpage come to mind, but I'm not sure how to marry them together. But maybe there is a simpler way to do this.
Extra Observations:
Thinking about this a little bit, tmux list-panes -a seems to list all the panes in the current session. Pretty useful to start off with. Where do I go from here?
Have you tried following in tmux window with multiple panes
Ctrl-B :
setw synchronize-panes on
clear history
A bit late to the party but I didn't want to set and unset synchronize-panes just to send one command so I created a wrapper function around tmux and added a custom function called send-keys-all-panes.
_tmux_send_keys_all_panes_ () {
for _pane in $(tmux list-panes -F '#P'); do
tmux send-keys -t ${_pane} "$#"
done
}
I also create a wrapper around the tmux command to simplify calling this function (for convenience). The wrapper and the above code are all here.
This allows me to run tmux send-keys-all-panes <command> or tmux skap <command to send <command> to all panes.
Note that tmux is aliased to my wrapper function tmux_pp.
Update June 2019
Quick illustration on how to configure your own binding for synchronize panes.
Added the following into my tmux.conf (the comments certainly apply to my overall configuration):
# synchronize all panes in a window
# don't use control S, too easily confused
# with navigation key sequences in tmux (show sessions)
unbind C-S
bind C-Y set-window-option synchronize-panes
Now, I can toggle the ability to synchronize commands across multiple panes with <C-a><C-y>.
(Yes, I remapped the bind key to Ctrl a).
my tmux version is 1.9a, and this works for me, one key is enough for both on and off
bind-key X set-window-option synchronize-panes\; display-message "synchronize-panes is now #{?pane_synchronized,on,off}"
None of the above answers worked for me (tmux v2.3), but this did, from the bash command line:
for _pane in $(tmux list-panes -a -F '#{pane_id}'); do \
tmux clear-history -t ${_pane} ; done
A more generalized script, for tmux commands other than 'clear-history' would just replace that element with a parameter, eg. $1. Do be careful if you intend to write a script to handle a series of tmux commands, as "-t ${_pane}" will need to be applied to each.
Note that the -a parameter to tmux list-panes is required to cover all panes in all windows in all sessions. Without that, only panes in your current tmux window will be affected. If you have more than one tmux session open and only want to apply the command to panes within the current session, replace -a with -s (It's all in the tmux man page).
I haven't the mod points to comment directly on each of the above answers, so here's why they weren't working for me:
The problem that I had with #shailesh-garg 's answer was that the sync affected only commands issued within the panes, not tmux commands issued using Ctrl-B : which are outside the panes.
The three problems that I had with #kshenoy 's answer were that:
it sends keystrokes to within a pane, not to the tmux operation
of that pane, so for instance, if one had a bash shell running in
the pane and one used the script to send "clear-history", those
would be the keystrokes that would appear in the bash command-line.
A work-around would be to send "tmux clear-history" or to pre-pend
"tmux " to "$#", but I haven't edited the answer because of my other
problems with the answer;
I couldn't figure out how to send a
new-line character without literally breaking the line;
Even when I did that, sending "tmux clear-history" had no effect.
If you want to send your command to every pane in every window in every session, add this to your .bashrc:
send_command_to_every_pane() {
for session in `tmux list-sessions -F '#S'`; do
for window in `tmux list-windows -t $session -F '#P' | sort`; do
for pane in `tmux list-panes -t $session:$window -F '#P' | sort`; do
tmux send-keys -t "$session:$window.$pane" "$*" C-m
done
done
done
}
You can then use it like this:
send_command_to_every_pane source ~/.bash_profile
Change "$*" to "$#" if you want that behavior, but in my experience this is what you want.
tmux send-keys -t <session id> <command> C-m
Replace the "session id" and "command" accordingly.
This is my utility function to do it, only executing the command when there there is nothing running in the pane.
#!/bin/bash
_send_bash_command_to_session() {
if [[ $# -eq 0 || "$1" = "--help" ]] ; then
echo 'Usage: _send_bash_command_to_session $session_name what ever command you want: '
return
fi
input_session="$1"
input_command="${#:2}"
for _pane in $(tmux list-panes -s -t ${input_session} -F '#{window_index}.#{pane_index}'); do
# only apply the command in bash or zsh panes.
_current_command=$(tmux display-message -p -t ${input_session}:${_pane} '#{pane_current_command}')
if [ ${_current_command} = zsh ] || [ ${_current_command} = bash ] ; then
tmux send-keys -t ${_pane} "${input_command}" Enter
fi
done
}
tmux_set_venv() {
_current_session=$(tmux display-message -p '#{session_name}')
_send_bash_command_to_session ${_current_session} workon $1
}
Example targeting a session called dev, enabling a python virtualenv in all panes that are in bash or zsh, avoiding executing the command in panes with vim or any other executable:
_send_bash_command_to_session dev workon myvirtualenv
or easier to remember: to do it in the current session:
tmux_set_venv myvirtualenv
Find my configuration file with this function.
You can combine synchronize-panes and send-keys in a single shortcut to send commands to all the panes:
Predefined tmux command clear-history:
bind-key C set-option -w synchronize-panes on\; clear-history \; set-option -w synchronize-panes off
Prompt an arbitrary tmux command:
bind-key p command-prompt -p "Panes command: " "set-option -w synchronize-panes on; %% ; set-option -w -u synchronize-panes"
Prompt an arbitrary shell command:
bind-key p command-prompt -p "Panes command: " "set-option -w synchronize-panes on; send-keys %%\\n ; set-option -w -u synchronize-panes"
By default, byobu uses tmux as backend. It's a wrapper that make things much easier:
Shift+F9:
Ctrl+F9:
Shift+F1
Admittedly only semi-related, I found I could make the status background red when I toggle synchronize-panes so it's obvious when I switch back to a window with an unknown synchronize-panes state:
bind-key C-x setw synchronize-panes on \; set-window-option status-bg red \; display-message "pane sync on"
bind-key M-x setw synchronize-panes off \; set-window-option status-bg default \; display-message "pane sync off"

How to create new tmux session if none exists

I am trying to figure out how to attach to a tmux session if a named tmux session exists, if not I want to create a new one with the given name.
Currently, I know of a few tmux commands which can partly achieve what I am looking for, but its not clear how to combine them together to get what I am looking for:
tmux attach attaches to an automatically existing session - but errors out if no session exists
tmux new creates a new session - but it does so every time, so I can't leave it in my .tmux.conf
tmux has-session tests whether a session exists - but I don't know how to stitch it together with the other commands
Thus, I would like to create a tmux script, so that this happens automatically, instead of having to manually create it everytime I need to log into a sessions.
How can I write a automatic script so as to create a new tmux session (if a given session name doesnt exist) or attach to a session name (if it exists)?
I figured it out (and had it pointed out to me).
tmux attach || tmux new
Alternately, you can add
new-session
to your .tmux.conf - that will create a default session on server start.
Then tmux attach will either attach to the current session (running server, that is), or create a new session (start the server, read the config file, issue the new-session command) and attach to that.
As pointed out in comments from Petr Viktorin, jkoelker and pjincz, you can use the following command to attach to mySession if it exists, and to create it if it doesn't:
tmux new -A -s mySession
From man tmux:
new-session[-AdDEP] [-cstart-directory] [-Fformat] [-nwindow-name] [-ssession-name] [-tgroup-name] [-xwidth] [-yheight] [shell-command]
(alias: new)
Create a new session with name session-name.
[...]
The -A flag makes new-session behave like attach-session if session-name already exists; in this case, -D behaves like -d to attach-session.
new-session has supported -A since tmux-1.8.
Adapting Alex's suggestion to include project based configuration upon startup, I started using the following:
# ~/bin/tmux-myproject shell script
# The Project name is also used as a session name (usually shorter)
PROJECT_NAME="myproject"
PROJECT_DIR="~/myproject"
tmux has-session -t $PROJECT_NAME 2>/dev/null
if [ "$?" -eq 1 ] ; then
echo "No Session found. Creating and configuring."
pushd $PROJECT_DIR
tmux new-session -d -s $PROJECT_NAME
tmux source-file ~/bin/tmux-${PROJECT_NAME}.conf
popd
else
echo "Session found. Connecting."
fi
tmux attach-session -t $PROJECT_NAME
where tmux-myproject.conf is my startup series of tmux commands to create my windows and panes, as well as start my editors.
Although I find rampion's answer is sufficient for using 1 session, this script lets you setup multiple sessions:
SESSIONS="work play"
function has-session {
tmux has-session -t $1 2>/dev/null
}
function except
{
if [ "$?" -eq 1 ] ; then
$1
fi
}
# Configure your sessions here
function session-work
{
tmux new-session -d -s work
tmux neww -k -t work:1
}
function session-play
{
tmux new-session -d -s play
tmux neww -k -t play:1
}
#
#MAIN
for x in $SESSIONS
do
echo $x
has-session $x
except session-$x
done
NOTE:
-k --> new-window will not be created if already exists
-d --> start session or window, but don't attach to it yet
-s --> name the session
-t --> specify a target location in the form session:window.pane
I use an alias to create a new session if needed, and attach to my default session if it already exists:
alias tmuxre='tmux new-session -t default || tmux new-session -s default'
I added this to my .login on my server.
The reason I do it this way is because I don't want to attach to the same actual session, I want a new session which uses the same group of windows.
This is also similar to running screen -xRR.
For those who want to do the same thing in fish:
tmux attach -t mysesh; or tmux new -s mysesh
(edit: A solution considering named sessions is mentioned at the end of this answer)
I came across this question when I was looking for a particular use-case, but couldn't find any solutions to it, so I'll add mine here:
Upon terminal-launch tmux should:
check whether there are any unattached sessions and use the first it can find (each session will be attached only once)
if there are no unattached sessions create a new one
After reading through the tmux man-pages and looking up arrays in bash I was able to come up with the following one-liner:
tmux attach -t ${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[1]} || tmux new
Explanation:
tmux attach -t $A:
attach to session with content of variable A (in our case the return value of the list-session command + array-index call)
tmux new:
create new session
together -> tmux attach -t $A || tmux new:
if tmux attach fails, create a new session
The next part (our $A) is finding an unattached session:
A = ${$B[1]}: return the second element in the list B (first one seems to always be an empty string)
B = tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}'
tmux list-sessions: list all sessions
tmux list-sessions -F '#{session_name}: -F stands for format and -F '#{session_name}' tells tmux to only show the name of the session and nothing else when it outputs the list
tmux list-sessions -f '#{==:#{session_attached},0}': -f stands for filter and -f '#{==:#{session_attached},0}'tells tmux to show only those list-elements, where the session_attached-value is equal to 0
tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}': both flags in combination will output only the session name and only for those elements of the list, where the session_attached-value is equal to 0 (=unattached sessions)
Example:
My application was for WSL, so I added it to the launch of my Ubuntu profile inside the settings.json of Windows Terminal:
"commandline": "C:\\Windows\\system32\\wsl.exe -d Ubuntu-22.04 tmux attach -t ${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[1]} || tmux new",
Edit:
If you have ([a-zA-Z]) named sessions you can sort the list to put those at the beginning:
tmux attach -t ${$(sort -n <<<"${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[*]}")[1]} || tmux new
If you add tmux new -s "primary" || at the beginning of the previous command it will try to create the session with the name "primary" and if it already exists it will only attach to it if it is still unattached, otherwise it will take another unattached session (prioritizing named over unnamed) or just create a new unnamed session if there are no unattached sessions left.
Caveat: each time you run this command and "primary" already exists it will output an error-message that it wasn't able to create a session called "primary" (only visible for a split second)
edit edit:
you can redirect those messages by using &> /dev/null:
(tmux new -s primary || tmux new -s secondary || tmux new -s tertiary) &> /dev/null || tmux attach -t ${$(sort -n <<<"${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[*]}")[1]} || tmux new"

Resources