Keeping the old conda env activated upon splitting panes in tmux - anaconda

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.

Related

How to create and set a system-wide environmental variable in Ubuntu through makefile?

I'm trying to create a system-wide environmental variable TEST_ENV_ONE.
I want to use it right after executing makefile without logout and after rebooting. So I'm trying to repeat manual moves like export variable and write it ti /etc/environment
I wrote a makefile like this, but it doesn't work:
var_value := some_string
TEST_ENV_ONE := $(var_value)
vars:
$(shell export TEST_ENV_ONE=$(var_value))
grep 'TEST_ENV_ONE=' /etc/environment || "TEST_ENV_ONE=\"$(var_value)\"" | sudo tee -a /etc/environment > /dev/null
What you want to do is basically impossible on a POSIX system as you've stated it. The environment of a process is inherited from its parent (the process that started it) and once a process is running, its environment cannot ever be changed externally. That includes by its children, or by modifying some other file.
You can, by modifying /etc/environment, change the environment for new logins but this will not change the environment of any existing shell or its child.
That being said, your makefile also has a number of problems:
$(shell export TEST_ENV_ONE=$(var_value))
This is doubly-not right. First, it's an anti-pattern to use the make $(shell ...) function inside a recipe script. Recipes are already shell scripts so it's useless (and can lead to unexpected behavior) to use $(shell ...) with them.
Second, this is a no-op: what this does is start a shell, tell the shell to set an environment variable and export it, then the shell exits. When the shell exits, all the changes to its environment are lost (obviously, because it exited!) So this does nothing.
Next:
grep 'TEST_ENV_ONE=' /etc/environment || "TEST_ENV_ONE=\"$(var_value)\"" | sudo tee -a /etc/environment > /dev/null
This does nothing because the statement "TEST_ENV_ONE=\"$(var_value)\"" sets an environment variable but generates no output, so there's no input to the sudo tee command and nothing happens. I expect you forgot an echo command here:
grep 'TEST_ENV_ONE=' /etc/environment || echo TEST_ENV_ONE=\"$(var_value)\" | sudo tee -a /etc/environment > /dev/null
However as I mention above, modifying /etc/environment will only take effect for new logins to the system, it won't modify any existing login or shell.

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

tmux active on shell startup gives nested tmux session for root

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?

How to show a tmux environment variable in the status bar (as window format)

What it comes down to is, I would like to
print a variable unique to the window ,or
run a script unique to the window
and use the output in the status bar such that each window status is unique. I've tried to make it more clear through 2 scenarios:
Scenario 1
I'm trying to print a unique per window variable in the status bar. I've opened a shell inside tmux and have stored the tmux environment variable locally (per session) and globally (for all sessions) with the following commands, respectively:
bash> tmux set-environment TMUX_STATUS_1 localvalue1
bash> tmux set-environment -g TMUX_STATUS_1 globalvalue1
I can verify these values for instance by going the another shell (in the same session) and typing:
bash> tmux show-environment TMUX_STATUS_1
TMUX_STATUS_1=localvalue1
bash> tmux show-environment -g TMUX_STATUS_1
TMUX_STATUS_1=globalvalue1
I've tried to print the above value in the statusbar (both the local and global value) and have configured the window format as follows:
WINDOW='[#(tmux show-environment -g TMUX_STATUS_#I 2>&1)]'
setw -g window-status-current-format $WINDOW
setw -g window-status-format $WINDOW
Initially it only showed windows as '[]', after this I added the redirection of stderr to stdout and got the status bar showing the following:
[unknown variable: TMUX_STATUS_1] [unknown variable: TMUX_STATUS_2] [...
What needs to be changed to make the statusbar show (according to previous commands):
[globalvalue1] [unknown variable: TMUX_STATUS_2] [...
PS: it's not a status bar refresh issues, as i've used the following command after setting the variable to manually force a refresh of the statusbar:
tmux refresh-client -S
Scenario 2
I've written a small bash script called 'tmuxscript' containing only:
echo "$(date '+%S') window:$(tmux display -p '#I') args:$#"
I updated my PATH variable and I have changed the window format to '[#(tmuxscript arg1 #I)]'. The output looks like:
[47 window:1 args:arg1] [47 window:1 args:arg1] [...
The time updates nicely. Printing the window index inside the script doesn't seem to work. The number 1 represents the window index of the currently focused window. This value is set for all windows, which is not what I want. I would at least expect to see (note the window index number) :
[47 window:1 args:arg1] [47 window:2 args:arg1] [...
Also, #I isn't getting past to the script, but the text 'arg1' is. How can I pas tmux variables to the script?
EDIT: I have now also tried setting the window status to:
'[#(tmux show-environment -g TMUX_STATUS_$\(tmux display -p "#I"\) 2>&1 | sed "s:^.*=::" )]'
Which gives me the following when the active (focused) window index is 1:
[globalvalue1] [globalvalue1] [...
Any help is appreciated!
Ok, I figured it out.
The problem lies in the fact that you are obligated to use the -g (global) flag when specifying a window-status in .tmux.conf.
.tmux.conf:
WINDOW="[#I #20W]"
set-window -g window-status-current-format $WINDOW
set-window -g window-status-format $WINDOW
The key is to make the status local after creating a window. Also, each window needs to be uniquely identifiable. Luckily this can be done by tmux variable 'window_id'. A small script, shown below, will output a variable unique to the window with this id as its first argument:
~/tmuxstatus:
#!/bin/bash
VARIABLE="W_$1"
VALUE=$(tmux show-environment -g $VARIABLE 2>&1)
VALUE=${VALUE#*=}
echo $VALUE
There is probably a TMUX only solution to make the status local, but I don't currently have the time. I'm using bash to do it with the aid of the environment variable PROMPT_COMMAND, which is evaluated just before the prompt is shown.
.bashrc:
function __prompt_command (){
if [ -n "$TMUX" ] && [ ! -n "$TMUX_INIT" ]; then
W=$(tmux display -p '#{window_id}')
VARIABLE="W_$W"
VALUE="value_$W"
STATUS="[#I #(~/tmuxstatus $W)]"
tmux set-option quiet on;
tmux set-environment -g $VARIABLE $VALUE;
tmux set-window window-status-current-format "$STATUS";
tmux set-window window-status-format "$STATUS";
export TMUX_INIT="done";
fi;
}
export PROMPT_COMMAND=__prompt_command
When changing the value of W_id, the window status changes also. It looks like:
[1 value_#0] [2 value_#1] [3 value_#2] [4 value_#3]
enjoy!
Perhaps using the echo command you can manually set the title using this syntax:
echo -ne "\033]0;$(tmux show-environment TMUX_STATUS_1)\007"
Try running that in different TMUX windows and see if it changes the title.

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