How to exit tmux without leaving my terminal - bash

I'm using tmux on bash, and letting it start automatically from .bashrc. Sometimes I want it disabled, and I should edit my .bashrc to do so. Editting a file everytime I disable tmux is quite troublesome, and I think the easiest way to do the same thing is exiting tmux without leaving terminal. Can I do that?
When I type exit, bash and terminal close. I tried exec bash , but it just restarted bash inside tmux.
I start tmux from code below, according to https://wiki.archlinux.org/index.php/tmux#Bash.
if [[ $DISPLAY ]]; then
# If not running interactively, do not do anything
[[ $- != *i* ]] && return
[[ -z "$TMUX" ]] && exec tmux
fi
If I just run tmux in code above instead of exec tmux, I can achieve my goal. But I don't like that, because I don't understand why the code uses exec tmux rather than tmux and don't wanna change it rashly, and when I run tmux I shoud type exit or C-d twice in order to exit terminal.

(Note: this question should really be on unix.stackexchange.com). One simple solution is to replace the line
[[ -z "$TMUX" ]] && exec tmux
with
[[ -z "$TMUX" ]] && { tmux; [ ! -f ~/dontdie ] && exit || rm ~/dontdie; }
This runs tmux as before, but when it exits goes on to test for the existence of a file, ~/dontdie. If the file does not exist the && exit is done and the terminal closes as before. If, however, you create the file before leaving tmux, then you will do the || rm ... part, which removes the file, and continues through the rest of the .bashrc file, leaving you in the bash shell.
So, to stay in the terminal, from the tmux window you type the commands:
touch ~/dontdie; exit
instead of just exit, and you will exit tmux and continue in bash.
To make it easier you can add a binding in ~/.tmux.conf to a key, such as X:
bind-key X send-keys 'touch ~/dontdie; exit' Enter
Then you simply type your prefix character, Control-B by default, then X to create the file and exit tmux.

Related

How to completely close terminal (Ubuntu bash) from TMUX session?

Can someone please help as how can I close the bash terminal completely from inside of TMUX session (closing TMUX session as well)?
Below code is from my .bashrc where TMUX session starts as soon as terminal launches
if command -v tmux &> /dev/null && [ -z "$TMUX" ]; then tmux attach -t default -c "$HOME" || tmux new -s default -c "$HOME" fi
Thanks!
You can use tmux detach -P to both detach and send HUP to the parent process, which should make it exit as well.

Run command in new background tmux window and wait for process to finish

I'm trying to use tmux in a script, so that it runs a command that takes some time (let's say 'ping -c 5 8.8.8.8', for example) in a new hidden pane, while blocking the current script itself until the ping ends.
By "hidden pane", I mean running the command in a new pane that would be sent in background, and is still accessible by switching panes in order to monitor and/or interact with it (not necessarily ping).
(cf. EDIT)
Here is some pseudo bash code to show more clearly what I'm trying to do:
echo "Waiting for ping to finish..."
echo "Ctrl-b + p to switch pane and see running process"
tmux new-window -d 'ping -c 5 8.8.8.8' # run command in new "background" window
tmux wait-for # display "Done!" only when ping command has finished
echo "Done!"
I know the tmux commands here don't really have any sense like this, but this is just to illustrate.
I've looked at different solutions in order to either send a command in background, or wait until a process has finished in an other pane, but I still haven't found a way to do both correctly.
EDIT
Thanks to Nicholas Marriott for pointing out the -d option exists when creating a new window to avoid switching to it automatically. Now the only issue is to block the main script until the command ends.
I tried the following, hoping it would work, but it doesn't either (the script doesn't resume).
tmux new-window -d 'ping -c 5 8.8.8.8; tmux wait -S ping' &
tmux wait $!
Maybe there is a way by playing with processes (using fg,bg...), but I still haven't figured it out.
Similar questions:
[1] Make tmux block until programs complete
[2] Bash - executing blocking scripts in tmux
[3] How do you hide a tmux pane
[4] how to wait for first command to finish?
You can use wait-for but you need to give it a channel and signal that channel when your process is done, something like:
tmux neww -d 'ping blah; tmux wait -S ping'
tmux wait ping
echo done
If you think you might run the script several times in parallel, I suggest making a channel name using mktemp or similar (and removing the file when wait-for returns).
wait-for can't automatically wait for stuff like pane or windows exiting, silence in a pane, and so on, but I would like to see that implemented at some point.
The other answers are only working if you're already within a tmux session.
But if you are outside of it you've to use something like this:
tmux new-session -d 'vi /etc/passwd' \; split-window -d 'vi /etc/group' \; attach
If you want to call this within a script you should check whether or not "$TMUX" is set. (Or just unset to force a nested tmux window).
#!/bin/sh
export com1="vi /etc/passwd"
export com2="vi /etc/group"
if [ -z $TMUX ]
then
export doNewSession="new-session -d 'exit 0'"
else
export doNewSession=""
fi
tmux $doNewSession \; split-window -d $com1 \; split-window -d $com2 \; attach;
[ -z $TMUX ] && exit 0
My solution was to make a named pipe and then wait for input using read:
#!/bin/sh
rm -f /wait
mkfifo /wait
tmux new-window -d '/bin/sh -c "ping -c 5 8.8.8.8; echo . > /wait"'
read -t 10 WAIT <>/wait
[ -z "$WAIT" ] &&
echo 'The operation failed to complete within 10 seconds.' ||
echo 'Operation completed successfully.'
I like this approach because you can set a timeout and, if you wanted, you could extend this further with other tmux controls to kill the ongoing process if it doesn't end the way you want.

Exit if in a script, do not exit if using a terminal/tty

If the user is entering commands in a terminal, I want to echo an error statement, but I do not want the terminal to close, so I have this:
if [[ "$fle" =~ [^a-zA-Z0-9] ]]; then
echo "quicklock: lockname has invalid chars - must be alpha-numeric chars only."
if [[ -t 1 ]]; then
# if we are in a terminal just return, do not exit.
return 1;
else
exit 1;
fi
fi
however the if [[ -t 1 ]]; then does not seem to work, the terminal window I am using just closes immediately, so I think exit 1 is being called.
The -t flag checks if any of the standard file descriptors are open, and specifically [ -t 1 ] will represent if the STDOUT is attached to tty, so when running from the terminal, it will always assert this condition as true.
Also the return keyword is applicable only when running a function to break out of it instead of terminating the shell itself. Your claim of terminal window closing because of hitting exit 1 when running from script, could happen only if you source the script, (i.e. in the same shell) and will not happen if you execute the script in a sub-shell.
You can use a construct for a no-action in scripts by just doing : in the if condition as
if [[ -t 1 ]]; then
# if we are in a terminal just return, do not exit.
:
Also -t is defined by POSIX because of which you can do just [ -t 1 ].
This is actually what ended up working for me:
function on_conditional_exit {
if [[ $- == *i* ]]; then
# if we are in a terminal just return, do not exit.
echo -e "quicklock: since we are in a terminal, not exiting.";
return 0;
fi
echo -e "quicklock: since we are not in a terminal, we are exiting...";
exit 1;
}
the test is to see if we are in terminal or in a script somewhere...if we are interactive, we are in a terminal..

URxvt as a shell-to-run-things or how to exit bash by <Enter>, but execute string entered so far?

What am I looking for is a replacement for all those things you’ve probably seen by hitting Mod+R in various tiling WM or Alt+F2 in Gnome DE — a small window to run things from there. I feel very sad without having my bash aliases and other stuff missing because those shells (at least those ones I can use now) are non-interactive and their interactiveness can’t be set as an option at run time.
This is why I decided to use URxvt window as a ‘shell for one command’. In my WM I have Mod+R shortcut bound to execute
/bin/bash -c 'export ONE_COMMAND_SHELL=t && urxvt -name one_command_shell'
And put
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"exit"'
in my ~/.bashrc
This way I can distinguish an URxvt instance which will become ‘shell for one command’, and set C-m combination (yes, it also works for Enter) to exit the shell, and, therefore, URxvt. The problem is how to execute string entered so far before exit?
I found two potential clues:
a) Making use of BASH_COMMAND
BASH_COMMAND
The command currently being executed or about to be executed, unless the
shell is executing a command as the result of a trap, in which case it is
the command executing at the time of the trap.
But I didn’t get it working because
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"exec $BASH_COMMAND exit"'
will cause exec exec exec.
b) A trap on SIGCHLD!
It may work for you, if you place
[ -v ONE_COMMAND_SHELL ] && {
PS1=
eaoc() { exit; }
trap eaoc SIGCHLD
}
At the end of your ~/.bashrc. PS1 may be non-empty, but it may not have subshell calls, or the trap will execute and shell will exit before the prompt is to be generated. This holds URxvt open till the forked process exit, so may be considered a half-way to solution.
c) A trap to DEBUG.
DEBUG trap executes before any simple command to be executed and before the first command in each function. If we provide a number via OC_SHELL and will count the number of executed commands, like…
/bin/bash -c 'export OC_SHELL=0 && urxvt -name one_command_shell'
# note a zero ----------------^
And at the end of ~/.bashrc
[ -v OC_SHELL ] && {
export PS1=
eaoc() { echo $OC_SHELL && [ $(( OC_SHELL++ )) -ge 1 ] && wait $! && exit; }
trap eaoc DEBUG
}
We get a fail again. Because our process, forked like
$ gimp &
Dies with its parent. How to deal with it?
Interesting thing is that DEBUG trap executes right after the command was entered, this trap does not wait for a process to return its status code or pid of background process in case of using &.
Ok, that’s how create process independent of bash Need to somehow wrap entered string into (nohup … &)
This works for me:
one_command_execute() {
eval $(echo "$READLINE_LINE") &
exit
}
[ -v ONE_COMMAND_SHELL ] && bind -x '"\C-m":"one_command_execute"'
Another version is without eval:
one_command_checkexit() {
[ -v ONE_COMMAND_DONE ] && exit
ONE_COMMAND_DONE=1
}
[ -v ONE_COMMAND_SHELL ] && PROMPT_COMMAND=one_command_checkexit
This doesn't close the window until the command exits. To execute everything in background automatically, add:
[ -v ONE_COMMAND_SHELL ] && bind '"\C-m":" & \n"'

How can I make TMUX be active whenever I start a new shell session?

Instead of having to type tmux every time, how could I have tmux always be used for new session windows?
So if I have no terminal windows open and then I open one, how can that first session be in tmux?
Seems like a .bashrc sort of thing perhaps?
warning this can now 'corrupt' (make it unable to open a terminal window - which is not good!) your Ubuntu logins. Use with extreme caution and make sure you have a second admin account on the computer that you can log into in case you have the same problems I did. See my other answer for more details and a different approach.
Given that warning, the simplest solution can be to append the tmux invocation to the end of your .bashrc, e.g.
alias g="grep"
alias ls="ls --color=auto"
# ...other stuff...
if [[ ! $TERM =~ screen ]]; then
exec tmux
fi
Note that the exec means that the bash process which starts when you open the terminal is replaced by tmux, so Ctrl-B D (i.e. disconnect from tmux) actually closes the window, instead of returning to the original bash process, which is probably the behaviour you want?
Also, the if statement is required (it detects if the current bash window is in a tmux process already) otherwise each time you start tmux, the contained bash process will attempt to start its own tmux session, leading to an infinite number of nested tmuxen which can be, err, quite annoying (that said, it looks cool).
However, there is a very small risk this can make bash behave in a way that other programs don't expect, since running bash can possibly cause it to turn into a tmux process, so it might be better to modify how you start your terminal emulator.
I use a small executable shell script ~/bin/terminal (with ~/bin in $PATH, so it is found automatically) that looks a bit like:
#!/bin/sh
exec gnome-terminal -e tmux
(I don't use gnome-terminal, so you might have to remove the exec, I'm not sure.)
Now whenever you run the terminal scipt you have a terminal with tmux. You can add this to your menu/desktop/keyboard shortcuts to replace the default terminal.
(This approach also allows you to more easily customise other things about the terminal emulator later, if you ever desire.)
My original, accepted answer, stopped working on my Ubuntu14 system after a recent upgrade.
Using either
[ -z "$TMUX" ] && command -v tmux > /dev/null && TERM=xterm-256color && exec tmux
or
[ $TERM != "screen" ] && TERM=xterm-256color && exec tmux
would stop me from being able to even login. I was only able to resolve this due to having a second admin login on the computer.
The fix for me on Ubuntu (and in osx too) was to change my terminal program to actually run tmux instead, i.e.
I still have
[ `uname -s` != Linux ] && exec tmux
as my last .bashrc line but that his only for my Mac OSX systems now.
If you want to have a single tmux session, put the following in your ~/.bashrc for bash or ~/.zshrc for zsh:
tmux attach &> /dev/null
if [[ ! $TERM =~ screen ]]; then
exec tmux
fi
The tmux attach line is to make sure if there is a session it attaches to and if there was no session you will not get the warning about "no session".
For me, I would love my tmux to be started every time I shell onto my remote machine, and when I detach or exit from tmux, the connection should be closed automatically. After digging into this issue for a while, the following code does exactly what I want and is believed to be the most optimized to the best of my knowledge.
[ -z "$TMUX" ] && { tmux attach || exec tmux new-session && exit;}
Note this line should be the first line in you bashrc file to make sure it is loaded first. We can't put an "exec" call in front of "tmux attach" because after the exec replaces the bash process with the tmux one, the connection will be closed even if there are no sessions to attach to. Therefore we need an "exit" call to terminate the connection after we detach or exit from the attached sessions. But putting an "exec" in front the new-session command is fine as that's the last command to be executed.
Append following line of code to the end of .bashrc,
[[ $TERM != "screen" ]] && exec tmux
I just made it a keyboard shortcut (in Linux Mint not Ubuntu; so I'm not sure if it is this easy)...
It might be hard to see, but the custom shortcut is gnome-terminal --window --maximize -e tmux. This starts a new gnome-terminal window maximized and then executes tmux for you.
I additionally have another custom shortcut that starts a "normal" gnome-terminal maximized (it's the same without the -e tmux).
I feel this is the best way because you can start whatever terminal whatever way you want and is the most customizable way.
I started with this https://wiki.archlinux.org/index.php/Tmux#Bash and enhanced it to reclaim detached sessions and make new ones if all sessions were already attached
# .bashrc
case $- in
*i*)
if command -v tmux>/dev/null; then
if [[ ! $TERM =~ screen ]] && [[ -z $TMUX ]]; then
if tmux ls 2> /dev/null | grep -q -v attached; then
exec tmux attach -t $(tmux ls 2> /dev/null | grep -v attached | head -1 | cut -d : -f 1)
else
exec tmux
fi
fi
fi
;;
esac
To enable tmux for login and ssh sessions, you can add this to the end of your .bashrc:
# enable tmux at login
PNAME="$(ps -o comm= $PPID)";
if [ $PNAME == "login" ] || [ $PNAME == "sshd" ] ; then
exec tmux
fi
This script looks for the parent process of the bash shell. If bash was started from logging in or from ssh, it will execute tmux. If you want this to work with a GUI terminal, you can add that in there as well. For example, if you want to start tmux automatically when you start Ubuntu's standard gnome-terminal, you would use this:
PNAME="$(ps -o comm= $PPID)";
if [ $PNAME == "login" ] || [ $PNAME == "sshd" ] || [ $PNAME == "gnome-terminal" ] ; then
exec tmux
fi
I've tested the above on Live Ubuntu Desktop and I was able to log in afterwards. This should not break the GUI login unless it invokes the login command to log in. I am not aware of a linux GUI that does this.
Within xfce4 (I'm running Fedora 24 XFCE spin, it's great), I've found the simplest solution is to edit panel shortcuts to so they run:
xfce4-terminal -e tmux
This same command can be used to replace the Keyboard Application Shortcut.
I had previously inserted an if statement into my .bashrc, but it caused login to fail (loop back to the login box whenever a correct password was entered).
The command for Thunar's Open Terminal Here command differs slightly. To change that goto:
Thunar > Edit > Configure Custom Actions... > Open Terminal Here > Edit button, and replace:
exo-open --working-directory %f --launch TerminalEmulator
with: xfce4-terminal --working-directory %f -e tmux
A one-liner that also makes sure the terminal type is set correctly for 256 colors:
[ -z "$TMUX" ] && export TERM=xterm-256color && exec tmux
How about adding
# If not running interactively, do not do anything
[[ $- != *i* ]] && return
[[ -z "$TMUX" ]] && exec tmux
to your .bashrc. It also works for zsh.
Taken from https://wiki.archlinux.org/index.php/Tmux#Start_tmux_with_default_session_layout

Resources