tmux active on shell startup gives nested tmux session for root - bash

I am using zsh together with tmux. I added to my user's .zshrc the following lines to auto start tmux every time i open a terminal.
[[ $- != *i* ]] && return
[[ -z "$TMUX" ]] && exec tmux
To get the same theme and settings for my root account i created a symlink between root its .zshrc and my own.
Now every time i login as root a new tmux session starts within the current one and a new shell window. Running pstree give this output:
tmux--zsh--sudo--su--tmux
|-zsh-pstree
I wonder why the last tmux process does not spawn a new shell? I expected it to be like
tmux--zsh--sudo--su--tmux--zsh
|-zsh-pstree
With the bash and its default settings logging in as root does not give me a new shell window. How do i need to change the setup to get this behaviour and how do i not start a new tmux session when changing to root?

Related

tmux: attach to existing session in new grouped session

I want to start tmux when opening a new terminal window via shortcut (ALT+F1).
tmux should start a new session when there is no session that I can attach to.
If there is an existing session, but I am not attached to it, simply attach.
If there is an existing session and I am already attached to it (i.e., I am opening a 2nd terminal window), I want to attach to the same session and be able to work on another window.
Currently, I am using the following script called tmux-init for the first two points:
#!/bin/sh
# Attach TMUX, or else open new session
if [[ -x "$(command -v tmux)" ]] && [[ -n "${DISPLAY}" ]] && [[ -z "${TMUX}" ]]; then
tmux attach || tmux >/dev/null 2>&1
fi
This automatically attaches to my existing session, or creates a new one when it does not exist.
However, I want to be able to open multiple windows and work on all of them at the same time.
Currently, all of the opened windows would be attached to the same session and mirror everything. The idea was to use tmux groups (tmux new-session -t 0), but my knowledge of bash scripting and tmux is limited. Here is what I currently have (tmux-init-2):
#!/bin/sh
tmuxtest=$(tmux ls | grep attached | grep "group 0")
if [[ ! -z "$tmuxtest" ]]
then
tmux new-session -t 0
tmux set-option destroy-unattached on
tmux new-window
else
tmux-init
fi
This works sometimes, sometimes it does not open a new window, sometimes it does not destroy the grouped session on detach... I can't seem to find a pattern. In most cases, the new window opens after I detach from the session. It seems that I should pass the options to tmux in a different way - but I have not found a way to create a new session and set options for that session immediately. Is there a better way to do it?

Display welcome message in new tmux session

In a bash script, a new tmux session is started, several processes are launched in separate panes, then the session is attached to the current terminal. I would like to display a short message that gives a few helpful hints how to navigate panes, exit the session etc.
I tried to just echo, but that is not executed inside the session. Is there a good approach how to do this?
Edit: my use case is to have everything contained in a repository. I want to avoid adding config to dotfiles or anything in the user's home dir.
There are a few ways you could print your message perhaps:
Make tmux enter copy mode with some output, for example: tmux new \; run 'echo hello'
Run a script in a new window that prints the output: tmux neww 'echo hello; sleep 5'
Make one of your panes (if your tmux is new enough) open a popup: tmux new 'tmux popup -KER "echo hello; sleep 5"; exec $SHELL"
You can check to see if there are tmux specific environment variables set in your .bashrc and print a message if they exist
eg. in your .bashrc
if [ ! -z "$TMUX" ]; then
echo "Your message"
fi

Keeping the old conda env activated upon splitting panes in tmux

I'm on tmux 2.6 and am using a few different conda environments. Upon splitting panes/windows I would like tmux to activate the environment of the parent pane also in the child pane.
I know that I can add code to be executed in a key bind for the split-window command and that the name of the currently active conda env is stored in $CONDA_DEFAULT_ENV. However, whatever commands I tried failed.
For my attempts I had conda activate $CONDA_DEFAULT_ENV in /some_path/bla.sh and set the pane-splitting command with
bind \ split-window -h -c "#{pane_current_path}" '/some_path/bla.sh'
in my tmux config file, but the newly created pane vanishes immediately after the split.
However, even if it had not, I guess it would have just reactivated the base env because the $CONDA_DEFAULT_ENV env variable has changed with initiating the new shell.
I guess a working solution has to first store the old $CONDA_DEFAULT_ENV environment variable, carry it over to the new tmux pane and then use it to set the conda environment, but I don't know how to achieve this.
I have accomplished this using tmux 3.1b. I see that you were running tmux 2.6 when you posted the question but, if you can, please try to update to at least v3.0, as my answer relies on the use of the -e flag to new-window and split-window and that flag was only introduced from v3.0 according to their changelog. Assuming you can use the aforementioned -e flag, the steps are as follows:
Step 1 (with tmux/tmux.conf):
Pass -e "TMUX_PARENT_PANE_ID=#{pane_id}", in addition to any other flags you may already use, when running tmux new-window or tmux split-window. This creates in the environment of the new (child) pane/window a variable TMUX_PARENT_PANE_ID that holds a unique ID assigned by tmux to the initial (parent) pane.
If you use key bindings, then you need to (re)write them in a not-very-intuitive manner making use of tmux's run. For instance, in order to bind letter "c" to tmux new-window, the corresponding line in your tmux.conf file must look like the following:
bind c run 'tmux new-window YOUR_CURRENT_OPTIONS -e "TMUX_PARENT_PANE_ID=#{pane_id}"'
where YOUR_CURRENT_OPTIONS represent whatever options you may already have in place prior to adding the -e option. If you just use bind c new-window YOUR_CURRENT_OPTIONS -e "TMUX_PARENT_PANE_ID=#{pane_id}", then #{pane_id} is passed as a literal "#{pane_id}" and the next steps won't work.
Setp 2 [in your bashrc (or equivalent) file]:
Redefine the conda command so as to:
Have a copy, in the local environment of each pane, of anaconda's CONDA_DEFAULT_ENV environment variable. In the code that follows, this copy will be named CONDA_DEFAULT_ENV_COPY. This will be useful for recalling the conda environment in a pane if bashrc (or your corresponding equivalent file) is sourced again.
Keep track of changes in the conda environments of all panes of the tmux session. We do this by defining/updating a TMUX_SESSION_CONDA_ENVS tmux session environment variable. Contrary to variables defined in the local environments of each pane, tmux session variables are accessible by all panes in the session.
The code to accomplish what is described above is:
# Redefine conda command, part a: Keep copy of original conda command/function
eval "original_$(declare -f conda)" 2> /dev/null
if [ $? -ne 0 ]; then
original_conda () {
command conda "$#"
}
fi
# Redefine conda command, part b: Add new functionality related to items (i) and (ii).
conda () {
# Run the regular conda
original_conda "$#"
local CONDA_RTN_CODE=$?
# Keep a copy of CONDA_DEFAULT_ENV to restore the environment if, e.g.,
# 'source ~/.bashrc' is run
CONDA_DEFAULT_ENV_COPY=$CONDA_DEFAULT_ENV
# Stop and return original_conda's return code if it fails
[ $CONDA_RTN_CODE -ne 0 ] && return $CONDA_RTN_CODE
# Do tmux-related stuff, but only if tmux is running and "$#" contains substring "activate"
if [[ -n "$TMUX" ]] && [[ "$#" =~ .*"activate".* ]]; then
# Create/update the *tmux* session env var "TMUX_SESSION_CONDA_ENVS"
local TMUX_SESSION_CONDA_ENVS=$(tmux showenv TMUX_SESSION_CONDA_ENVS 2>/dev/null)
if [[ $? -eq 0 ]]; then
# Get list of conda envs for all panes except the current one
local OLD_VALUES=$(echo $TMUX_SESSION_CONDA_ENVS | sed "s/TMUX_SESSION_CONDA_ENVS=//")
local CONDA_ENV_OTHER_PANES=$(echo $OLD_VALUES | sed "s/$TMUX_PANE:\w*[[:space:]]*//g")
fi
# Include current pane's conda env info
tmux setenv TMUX_SESSION_CONDA_ENVS "$TMUX_PANE:$CONDA_DEFAULT_ENV $CONDA_ENV_OTHER_PANES"
fi
}
Step 3 [also in your bashrc (or equivalent) file]:
Query the parent's pane conda environment at the time the child was created, and, finally, activate the conda environment in the child. The code I'm currently using is the following:
if [[ -n "$TMUX_PARENT_PANE_ID" ]]; then
# Remember: "TMUX_SESSION_CONDA_ENVS", as per our redefined "conda" command, carries
# info about changes in the the conda environments in all the session's panes.
# TMUX_PARENT_PANE_ID makes it thus possible to query, from any child
# pane, its parent's conda environment at the time the child was created.
# This is exactly what will be done now.
TMUX_SESSION_CONDA_ENVS=$(tmux showenv TMUX_SESSION_CONDA_ENVS 2>/dev/null)
if [ $? -eq 0 ]; then
PATT="(?<=${TMUX_PARENT_PANE_ID}:).*?(?=([[:space:]]|$))"
PARENT_CONDA_ENV=$(echo $TMUX_SESSION_CONDA_ENVS | grep -oP "$PATT" | head -1)
echo "Activate conda env '$PARENT_CONDA_ENV' of parent tmux pane '$TMUX_PARENT_PANE_ID'"
conda activate $PARENT_CONDA_ENV
fi
# Clean up the pane's env (TMUX_SESSION_CONDA_ENVS remains in the tmux session env)
unset TMUX_SESSION_CONDA_ENVS PATT PARENT_CONDA_ENV
# Erase memory of parent tmux pane's ID so that the 'else' block below
# is run if we re-source bashrc
unset TMUX_PARENT_PANE_ID
else
# Triger update of TMUX_SESSION_CONDA_ENVS and CONDA_DEFAULT_ENV_COPY
# when the pane has no parent (very first pane or a pane where bashrc was
# re-sourced after creation).
[[ -n "$CONDA_DEFAULT_ENV_COPY" ]] && echo "Activate previous conda env '$CONDA_DEFAULT_ENV_COPY'"
conda activate $CONDA_DEFAULT_ENV_COPY
fi
After following these steps, just close your terminals, open them again and relaunch tmux. I hope this helps.

bash script - how to execute command after starting new tmux session

For example: start new tmux session, launch webserver (sends log into console), and launch second tmux window for editor.
I had trying do this by different ways - but nothing works.
Take a look at tmuxify (disclaimer: I wrote it).
It's to automate the launch of tmux sessions by defining a layout file with windows, panes and commands.
For commands executions, take a look at this answer.
But you can also predefining layouts of tmux sessions, try tmuxinator/tmuxinator
.
You can run shell commands from ~/.tmux.conf:
if-shell shell-command export I_AM_USING_TMUX=1
But it often does not seem to work as expected...
The environment variable TMUX is set when you run tmux. Then you can conditionally run shell commands from ~/.bash_profile:
if [[ ! -z "$TMUX" ]]; then
# run shell commands
fi
Or, you can export your own environment variable from ~/.tmux.conf file (as I already exported one from the beginning) and use it for if condition from ~/.bash_profile again:
if [[ ! -z "$GHCHOI_IS_USING_TMUX" ]]; then
# run shell commands
fi

How to launch tmux –automatically– when konsole/yakuake start?

I discovered tmux possibility recently and I'm using it inside yakuake/konsole (quake-like terminal). However, I have to launch tmux manually every time I start my laptop or restart yakuake.
How to launch tmux –automatically– when yakuake/konsole start ?
A friend advice to use <terminal_emulator> -e tmux.
Konsole
It works with konsole.
I modified the property in the menu to:
konsole -e tmux
Yakuake
However it doesn't work with yakuake.
Based on the Start tmux on every shell login article from the Archlinux wiki, you can start tmux on your shell with the following code at the
Zsh or Bash
Add in your zsh or bash configuration (usually ~/.zshrc or ~/.bashrc) the following code and restart your session:
function start_tmux() {
if type tmux &> /dev/null; then
#if not inside a tmux session, and if no session is started, start a new session
if [[ $HOST == "laptop" && -z "$TMUX" && -z $TERMINAL_CONTEXT ]]; then
(tmux -2 attach || tmux -2 new-session)
fi
fi
}
start_tmux
Fish
Add in your fish configuration (usually ~/.config/fish/config.fish) the following code and restart your session:
function start_tmux
if type tmux > /dev/null
#if not inside a tmux session, and if no session is started, start a new session
if test -z "$TMUX" ; and test -z $TERMINAL_CONTEXT
tmux -2 attach; or tmux -2 new-session
end
end
end
start_tmux
I solved this by creating a Konsole/Yakuake profile (they're one and the same) + made it the default, in which I set up the Command to:
/usr/bin/sh -ilc "tmux attach || tmux new"
Manage profiles + where the profile is located, in case Yakuake/Konsole doesn't start up anymore:
When yakuake is running:
qdbus org.kde.yakuake /yakuake/sessions runCommandInTerminal 0 "tmux"
I haven't tried with Yakuake but I have a one liner shell scripting approach to make it work with Konsole Terminal Emulator.
Konsole emulator set KONSOLE_<something> environment variable on start.
Knowing this fact, we can add this to .zshrc file
[ -z "$KONSOLE_VERSION" ] || tmux
And this will start all KONSOLE windows attached to active tmux session or create one if its the first window.

Resources