How to create, name, and run a command in tmux automatically - bash

I use tmux for a program I run that sometimes crashes, so I can collect errors from it. I use this method of error collection for a few different programs and would like to know if there's a programmatic way of creating multiple tmux sessions with bash, each having their own name and having a command ran in them.
I have tried doing the following:
tmux new -ds "myname" "my command"
tmux new -ds "myname2" "my command"
however, if the program or command in the tmux session finishes/closes/crashes the tmux session is automatically exited and closed, defeating the purpose of trying to get error output.

You have to make the command not exit. Ex. run the shell after the command, so the shell will wait for you:
tmux new -d -s my-session 'sh -c "my command; sh"'

Related

gnome-terminal -- $SHELL fails to execute the simplest commands

gnome-terminal -v -- $SHELL launches a new bash shell just as expected.
gnome-terminal -v -- $SHELL -c 'cd /' does nothing at all. A window briefly flashes, and that's it. Tried many other commands, like echo with the same result.
journalctl -f reveals Failed to start VTE child process 11350 launched by gnome-terminal-server process 7182. and that's it.
How do I debug this further?
this may not be the exact solution you need, but if you make a new profile for the terminal, and change the property "when command exits" to "hold terminal open"
then include the following argument:
gnome-terminal --window-with-profile=[new profile name here]
the new window will not briefly flash, and may allow more detailed debugging from whatever processes are called in the new window

Executing new tmux session in shell script with exec "$#"

I only have a shell (sh, not bash) login over an SSH connection to a remote device. The script ends with exec "$#", which, as I understand, passes on all parameters as strings.
Now I want to execute that last command in a new tmux session, so that the program would continue in case of a connection loss.
This is for a robotics project, therefore I am assuming the connection to be partially interrupted.
How can I wrap this into a command, that will run exec "$#" in tmux?
I already tried:
tmux new "exec \"$#\""
and
tmux new -s session send-keys "exec \"$#\""
as well as both variations with only "exec $#"
However, nothing seems to work in shellchecker or my draft .sh files. I would be very grateful for any help.
Try:
exec tmux new -- "$#"
If it doesn't work, you can try something like:
exec tmux new -- sleep 10000
And try to attach to make sure tmux is being run (for example that the script can find tmux).
sh -x may also be useful to see what is actually being run.
Also remember that if tmux is already started the environment inside may not be the same as in the script.

How can I write a text file, and then read from it in the same Bash script?

I have a hotkey in Vim that takes me into command mode and calls a Bash script. The Bash script attempts the following steps:
Select text between two tokens
Send selected text block to a .py file
Load the .py file into IPython
The script works if I type everything in by hand, but if I run the script, the text block is not saved as a file until after the script is finished, causing an error when IPython tries to load the file later in the script.
Here are the steps I've tried:
In the event my file is being held in the buffer, I tried syncing and flushing
In case the file needed more time to be written, I tried sleep and wait
I also tried asynchronous shell commands to see if the Bash script was getting priority over Vim writing the file.
#!/bin/bash
# Text to be written has been selected in Vim
tmux send-keys ':w jtemp.py'
tmux send-keys 'Enter'
# Load code selection in IPython
tmux select-pane -t 1
tmux send-keys '%load jtemp.py'
tmux send-keys 'Enter'
tmux send-keys 'Enter'
How can I get the file to be saved to disk while the Bash script is still in process?
The problem is most likely (I don't know this for certain however) that Vim is blocking and not processing the key presses until the script finishes. I don't know if it is possible to make Vim run the script in the background, but you could if you bind the hotkey in tmux instead using run-shell -b, something like:
bind F1 run -b "bash /path/to/my/script"
In fact, if doing it in tmux you probably won't need the script to run in the background, because the two affected applications (Vim and IPython) will not block and it doesn't matter if tmux does. So you could just leave the -b out.
You will still almost certainly need a sleep of a second or half a second ("sleep 1" or "sleep 0.5", experiment) between the Enter and the select-pane or there will be a race between Vim writing the file and IPython reading it.
I am not sure of the overhead of calling tmux in a shell script, but I would recommend you script your workflow process without it—you can use sed to parse for text between two tokens in a file, pipe that to a new .py file, and then load that .py file into IPython.

Multiple tabs and Bash scripting

I am trying to open multiple tabs and execute a series of commands in each tab. Lets say I open 3 tabs tab1, tab2, tab3. Then in each tab I would like to execute following:
ssh user#address (PublicKey Authentication is setup and
hence no need to enter password)
Launch python scripts (python some.py)
Hold the tab open after executing the commands to see the outputs.
I went through some threads and have a rough outline for Bash script.
#!/bin/bash
echo "Script running"
gnome-terminal -e "bash -c \"ssh user#address; uname -a; exec bash\""
When I run the above script, a new terminal opens and I can see that I have ssh-ed into the target address but the other command uname -a didnot execute.
I would like to build upon this to implement the following:
Open multiple tabs and run commands. Ex : gnome-terminal --tab -e
"bash -c \"ssh user#address; python file1.py; exec bash\"" -tab -e
"bash -c \"ssh user#address; python file2.py; exec bash\""
Wait for one of the python file to start executing before opening
another tab and repeating the process for another python file.
Also is there a better way to implement the same task ?
The above code snippet was from this thread.
You should consider using screen or tmux or a similar terminal multiplexer for this.
Example usage:
screen -d -m bash -c 'ls; bash'
to initiate a screen session in which ls was executed and then a shell started, and then
screen -X screen bash -c 'date; bash'
to create a new window in the existing screen session, run date therein and then start a shell in that window.
Mind that the programs are run without you seeing their output right away on your controlling terminal. You can then attach to the screen session using
screen -x
Which attach you to the running session and will show you one of the screen windows (the virtual terminals of your two running programs). Typing Ctrl-A n will switch through the windows, Ctrl-A d will detach you again, leaving the programs running, so you can attach later with screen -x.
You can attach from several locations (e. g. from two different Gnome-terminals) to the same running windows. Both will then show the same contents.
Another advantage of using screen is that you can log out and the programs keep running. If you later login again, you can still attach to the running sessions.
Only a direct attack like a reboot, a kill-signal or an interaction (like pressing Ctrl-C while being attached) will terminate your programs then.

mosh: sources .bashrc twice with different PIDs

I use tmux in my build server. Recently I wrote a small .bashrc script that will automate attaching to tmux session if one exits. The script looks like follows
# Automate tmux Startup
if [ -z "$TMUX" ]; then
# we're not in a tmux session
if [ `ps -o comm= -p $PPID` == "sshd" ]; then
# even VNC can have $SSH_TTY and $SSH_CONNECTION set so we cant find out
# if we want to attach to tmux during ssh so we need to see if parent
# process is sshd see
# http://unix.stackexchange.com/questions/145780/linux-ssh-connection-is-set-even- without-sshing-to-the-server
# Only attach to tmux if its me
WHOAMI=$(whoami)
if tmux has-session -t $WHOAMI 2>/dev/null; then
tmux -2 attach-session -t $WHOAMI
else
echo "Start tmux with username as session name 'tmux new -s $WHOAMI' "
fi
fi #parent process check
else
echo "Inside tmux"
fi
The problem is whenever I ssh using mosh it just hangs inside the tmux window. I found that if I remove this script and then use mosh to just ssh and attach to tmux manually then I don't hit on this issue. This issue only happens if I place the above script in .bashrc.
My suspicion was that mosh is waiting for .bashrc to complete and waits for it indefinitely and in the mean time it also does not pass the mouse and keystroke controls over to tmux. I confirmed this by killing the tmux session from another terminal and found that mosh recovered and tried to execute my previously buffered keystrokes.
The strange thing is that how mosh managed to cross the ps -o comm= -p $PPID == "sshd" check. This is because for mosh the Process name of shell is bash and Parent process name of the shell is mosh-server and not sshd. Further investigation revealed that mosh executes .bashrc twice once as sshd and once as mosh-server. This is reproducible by putting ps -o comm= -p $PID -p $$ >> moshbash in .bashrc.My tmux attach was happening in sshd and hanging mosh forever.
A simple workaround I found was to do
mosh user#server -- tmux attach -t `whoami`
I can do a similar thing to my ssh as well to fire command from the client side and eliminate the .bashrc script altogether but I do not wish to spill over my server side automation to the client side.
Actually mosh does not seem to be wrong. It is sourcing a .bashrc file only once per PID .I think it is a bad design to have .bashrc kick of a blocking session to tmux since tmux also needs terminal we cant start off as a background process either so & also wont work.
Is there any other way around this problem ? I think if we can distingusih between a mosh client setting up a sshd and a ssh client setting up sshd that information can be used.
.bashrc always executes everytime an interactive non-login bash instantiates so use .bash_profile instead so it would only run once during login to ssh. If the script or processes of the script summons bash, it would cause repeated summoning.
See Bash Startup Files for more info and other startup files.

Resources