Prevent tmux from exiting when script finishes - shell

I'd like to prevent tmux from exiting automatically when the script finishes. For example, I have a command something like this(below), this does run a server for an app, but sometimes this unexpectedly exits with an error. When that happens, I'm unable to check what was wrong with the code, command.
tmux new-session -d -s "visited" 'bash -ic "visited --server"';
Following one is a simplified command of what I'd like to do.
tmux new-session -d -s "pwd" 'bash -c pwd' # but actually I need to load .bashrc
tmux a -t pwd # this should attach to the shell, but in my environment this does show "can't find session pwd" since the shell is already exited
I read a github issue and tried to add set exit-empty off to ~/.tmux.conf and restart the server tmux kill-server and re-tried the above, but seemed it doesn't work.

Just let tmux create a normal session and inject the keystrokes:
tmux new-session -d -s pwd
tmux send-keys pwd C-m
tmux a -t pwd

If you end your statement with && bash then it will drop you into a shell when completed succesful. If you want to do that only when command fails then instead use || bash.
E.g.
# Drops you into a shell if it fails, otherwise closes the window
tmux new-session -d -s "visited" 'bash -ic "visited --server" || bash';
# Drops you into a shell when finished successful, otherwise closes window.
tmux new-session -d -s "visited" 'bash -ic "visited --server" && bash';
# Always drops you into a shell when done.
tmux new-session -d -s "visited" 'bash -ic "visited --server" || bash && bash';

Related

How to start tmux session on the server by ssh and execute script in the session

I have a program on the server, which execution should be initiated from the client by bash script with the use of ssh. Moreover, this script should open the tmux session on the server, run the program with some argument and terminate the session after program return.
I tried several solutions, but no one has been successful.
1)
#! /bin/bash
argument='12345678'
ssh user#host << EOF
tmux new-session -t session1
./program $argument
tmux kill-session -t session1
EOF
The program executes on the server, but without tmux session.
Output: "open terminal failed: not a terminal"
2)
#! /bin/bash
ssh user#host -t 'tmux new-session -t session1'
As minimum, this command open the session (actually don't know what is the construction ssh user#host -t '...' and how it works. If someone explains, I will be grateful) and I can type commands manually. But I don't know how to make server run my program using the script as I plan. Please help me to find solution.
I think you want to create a "detached" tmux session, then use tmux send-keys to send command instructions to the session. Also, I would use the full path to program. E.g. something like this
ssh user#host << EOF
tmux new-session -t session1 -d
tmux send-keys -t session1 "/path/to/the/program $argument" C-m
tmux kill-session -t session1
EOF
Tell tmux to run the program for you.
ssh user#host "tmux new-session -t session1 './program \"$argument\"'"
This runs program instead of a shell in the (only) window of the new session. When the program exits, the window is closed and along with it the session ends. (Quoting can get a bit hairy, so I'm calling that out-of-scope for this question.)
You can start a new tmux session in detached mode and then send commands with tmux send-keys command:
#! /bin/bash
argument='12345678'
ssh user#host << EOF
tmux new-session -t session1 -d
tmux send-keys -t session1:1 "./program $argument && tmux -d" ENTER
EOF

Wait in bash script for env to load in tmux session

I have the following script:
#!/bin/sh
# Startup tmux session, activate env inside session, wait, execute invoke command
DIR=$(echo $DIR_HOME)
CLOUD=$'cloud_sql'
tmux new -s $CLOUD -d
printf "Starting: $CLOUD \n"
tmux send-keys "cd $DIR_HOME" C-m
tmux send-keys 'pipenv shell' C-m
printf 'Env Started! \n'
if [[ "-z ${ENV_ACTIVE}" ]]; then
tmux send-keys -t $CLOUD "wait 15" C-m
tmux send-keys -t $CLOUD 'inv gce.cloud-sql-proxy -p 5432' C-m
else
tmux send-keys 'Server not ready!'
fi
printf "$CLOUD started. Attach using: \n\n"
printf "tmux attach -t $CLOUD \n\n"
exit 0
I'd like for the pipenv shell command to finish before (inside the tmux session) running inv gce.cloud-sql-proxy .. but for some reason I see it executes those commands before/during the activation of pipenv shell..
On a side note, if I added a tmux attach -t $CLOUD before the if statement, it seemed to work although this was ideal and id like to exit the tmux session upon completion.
Thanks for your help!
I had the same problem, for me it was:
tmux send-keys "pipenv shell" C-m
tmux send-keys "python run.py" C-m
I solved it by appending the following to my Pipfile:
[scripts]
serve = "python run.py"
Then I changed my tmux to:
tmux send-keys "pipenv run serve" C-m
This runs my script called serve, i.e., runs a program using the env without needing to load it first. You can probably do the same by creating a script in Pipfile for inv gce.cloud-sql.proxy... and run that.

Tmux not responding to commands in shell script

I have a simple bash script that creates a new tmux session and does some layout:
#!/usr/local/bin/bash
tmux new-session -s $1
tmux split-window -h -p 50 -t 1
tmux new-window
tmux split-window -h -p 50 -t 1
The contents are in an executable script. When i execute the script with the name of the session as the argument, I get a new tmux session but there is only one un-split window, instead of the two [split] windows that I am telling it to create. If I run the scripts one by one on the shell prompt then I do get the desired outcome. So why is this not working in the script?
The problem is that first command starts tmux and wait for it to finish before continue. What you need to do is write custom tmux.conf file and add pass it through -f filename.conf with first command.
the other possible way is use tmux -d
#!/bin/bash
tmux new-session -d -s $1
tmux split-window -h -p 50 -t $1
tmux new-window -t $1
tmux split-window -h -p 50 -t $1
tmux attach -t $1

Starting tmux with certain command inside of bash script

I ran into problem when tried to start tmux inside of bash-script.
The following script is sample of the problem.
tmux new-session -d -s main
tmux send-keys -t main 'ls ~/' C-m
tmux attach-session -d -t main
This script works correct - it started tmux with list of ~/ directory.
Then I tried to start same command (ls ~/) as a variable
tmux new-session -d -s main
foo="'ls ~/'"
tmux send-keys -t main "$foo" C-m
tmux attach-session -d -t main
However, It didn't work. I have got the following message
'ls ~/'
$ 'ls ~/'
-bash: ls ~/: No such file or directory
What is the reason of this problem and how to fix it?
You can't stick quotes inside quotes and have the shell remove them for you correctly.
See mywiki.wooledge.org/BashFAQ/050 for a full discussion on this.
Drop one set of the quotes there.
Either foo="ls ~/" or foo='ls ~/' but not both.

Bash scripts with tmux to launch a 4-paned window

Can anyone help explain what's going on with tmux, bash, and exec? I'm trying to set up a tmux session with a 4-pane window. Ideally, I want to run a command in 3 of the panes: e.g. a Ruby Thin server and a couple of Ruby daemons. This is what I have so far:
~/.bin/tmux-foo:
#!/bin/sh
tmux new-session -d -s foo 'exec pfoo "bundle exec thin start"'
tmux rename-window 'Foo'
tmux select-window -t foo:0
tmux split-window -h 'exec pfoo "bundle exec compass watch"'
tmux split-window -v -t 0 'exec pfoo "rake ts:start"'
tmux split-window -v -t 1 'exec pfoo'
tmux -2 attach-session -t foo
~/.bin/pfoo:
#!/bin/bash
cd ~/projects/foo
rvm use ree
# here I want to execute command1 2 3 or 4...
exec $SHELL
It all works... but when I ctlr-c in the first pane that is running the thin server, it stops the thin server and returns to the shell. However, the command is not in the history; i.e. if I hit the up key I don't get the bundle exec thin start command... I get some other command from my bash history. I'm wondering if there's any way to arrange these scripts so that I get the commands in the bash history.
Also... I've tried many combinations of exec, exec $SHELL -s ..., and exec $SHELL -s ... -I and I'm not quite sure what is going on...
Can anyone help explain the general idea of what is going on with tmux and bash and exec here?
As others have mentioned, your commands are being run by the shell script before launching your $SHELL; there is no general way the instance of $SHELL can know what its parent ran before starting it.
To get the “initial command” into the shell history, you need to feed the command keystrokes directly to the instance of $SHELL itself (after it has been started, of course). In other contexts I might suggest using a small Expect program to spawn an instance of $SHELL, feed it the keystrokes, then use interact to tie the tty to the expect-spawned $SHELL.
But in the context of tmux, we can just use send-keys:
#!/bin/sh
tmux new-session -d -s foo 'exec pfoo'
tmux send-keys 'bundle exec thin start' 'C-m'
tmux rename-window 'Foo'
tmux select-window -t foo:0
tmux split-window -h 'exec pfoo'
tmux send-keys 'bundle exec compass watch' 'C-m'
tmux split-window -v -t 0 'exec pfoo'
tmux send-keys 'rake ts:start' 'C-m'
tmux split-window -v -t 1 'exec pfoo'
tmux -2 attach-session -t foo
tmuxinator lets you specify this with a nice yaml file. For your case you could have:
# ~/.tmuxinator/foo.yml
# you can make as many tabs as you wish...
project_name: foo
project_root: ~/projects/foo
rvm: ree
tabs:
- main:
layout: tiled
panes:
- bundle exec thin start
- bundle exec compass watch
- #empty, will just run plain bash
- rake ts:start
You can of course have extra windows etc.
Place the following into the command prompt [all as one line], it will open 4 tmux panels automatically (I know this wasn't the question, but this looks somewhat easier than what I saw posted):
tmux new-session \; \split-window -v \; \split-window -h \; \select-pane -t 0 \; \split-window -h
Now you can take that command and use it with whatever scripting language you like [you need to double the escape characters {backslash characters} if using perl...and probably other languages].
This runs the subsequent command in the newer tmux panel, reverting to the first and splitting it at the end.
You are running the command and then entering the interactive shell; the command run from the script, not being in an interactive shell, doesn't get recorded in the history. You really want a way to stuff (that's a technical term :) once upon a time it was TIOCSTI for "terminal ioctl(): stuff input") input for the shell into the window.
With tmux, it looks like you use buffers for this. Something along the lines of (untested)
#! /bin/bash
cd ~/projects/foo
rvm use ree
if [[ $# != 0 ]]; then
tmux set-buffer "$(printf '%s\n' "$*")" \; paste-buffer -d
fi
exec ${SHELL:-/bin/sh}
You can create a bash script like this.
#!/bin/sh
tmux new-session -d -s mysession
tmux send-keys -t mysession "cd ~" Enter
tmux split-window -h -t mysession
tmux send-keys -t mysession "watch -n 1 df -H" Enter
tmux split-window -v -p 50 -t mysession
tmux send-keys -t mysession "htop" Enter
tmux select-pane -t mysession:0.0
tmux split-window -v -p 50 -t mysession
tmux send-keys -t mysession "cd ~" Enter
tmux select-pane -t mysession:0.0
# Attach to session
tmux attach -t mysession
You can change commands inside "" in each send-keys command as per your preference.

Resources