# .tmux.conf
set -g status-utf8 on
setw -g utf8 on
Yet, still when I enter the scandinavian charaters æ, ø and å in the terminal window, I get the following output:
<00c3><00a6><00c3><00b8><00c3><00a5>
I'm using Iterm2 as my terminal, and the characters are printed correctly outside of tmux.
Are you using zsh? I was able to get it to produce a result like that (inside and outside tmux) by setting LC_CTYPE to en_US.US-ASCII while actually sending UTF-8 (i.e. lying to zsh (and other locale-sensitive programs) about what character set to expect).
Check that LC_ALL, LC_CTYPE, and/or LANG have an appropriate values inside your tmux session; you probably want to use a consistent value that ends with .UTF-8. You can use locale to view the active values and locale -a to list the available values.
You may also need to reset the errant variable(s) in your tmux global and/or sessions environments (so that new sessions/windows/panes do not keep getting the bad values). You can inspect the tmux global environment with
tmux show-environment -g | grep -E 'LC|LANG'
Adjust its values with (e.g.) tmux set-environment -g LANG "$your_value".
Each session can also override environment variables (for new windows and panes created in that session). You can inspect a session’s environment with
tmux show-environment -t "$session_name" | grep -E 'LC|LANG'
You can unset session environment values with (e.g.) tmux set-environment -t "$session_name" -u LANG (so that the global value will be used for new windows/panes), or adjust session values with tmux set-environment -t "$session_name" LANG "$your_value".
Or, if you do not have any important sessions, you could just restart the tmux server with a set of known-good locale environment variable values.
Related
When tmux opens, I would like it to use zsh instead of bash by default. How would I accomplish this?
From man tmux:
default-shell path
Specify the default shell. This is used as the login shell for new windows when the default-command option is set to empty, and must
be the full path of the executable. When started tmux tries to set a default value from the first suitable of the SHELL environment
variable, the shell returned by getpwuid(3), or /bin/sh. This option should be configured when tmux is used as a login shell.
So, in your tmux.conf:
# set shell
set -g default-shell /bin/zsh
and if you want you can add default command each time, when we start a new window:
# Retach userspaces
set -g default-command "reattach-to-user-namespace -l zsh"
You probably want zsh to be your default shell for most things, then (but this will not apply to cron). The following will make zsh your default shell, and you should then not need to tell tmux anything.
chsh -s /usr/bin/zsh
Note that some OSs still use /bin/zsh as the path to zsh.
If you prefer to set it individually for a session, but not for other (future) sessions, you can use
tmux new-session /bin/zsh \; set default-shell /bin/zsh
I work in a situation where I have multiple projects and within each are many scripts that make use of environment variables set to values specific to that project.
What i'd like to do is use a separate tmux session for each project and set the variables so that they are set for all windows in that session.
I tried to use the set-environment option which works using the -g option but then sets the variable for all sessions connected to that server.
Without the -g option I see its set when using show-environment but can't access the variable in the shell.
Has anyone come up with a way of fixing this?
Using tmux 1.8 and tcsh
I figured out a way to do this. I'm using tmux 2.5.
Background
In the tmux man page, it states that there are two groups of environment variables: global and per-session. When you create a new tmux session, it will merge the two groups together and that becomes the set of environment variables available within the session. If the environment variables get added to the global group, it appears that they get shared between all open sessions. You want to add them to the per-session group.
Do this
Step 1: Create a tmux session.
tmux new-session -s one
Step 2: Add an environment variable to the per-session group.
tmux setenv FOO foo-one
This adds the environment variable to per-session set of environment variables. If you type tmux showenv, you'll see it in the output. However, it isn't in the environment of the current session. Typing echo $FOO won't give you anything. There's probably a better way to do this, but I found it easiest to just export it manually:
export FOO='foo-one'
Step 3: Create new windows/panes
Now, every time you create a new window or pane in the current session, tmux will grab the FOO environment variable from the per-session group.
Automating it
I use bash scripts to automatically create tmux sessions that make use of these environment variables. Here's an example of how I might automate the above:
#!/bin/bash
BAR='foo-one'
tmux new-session -s one \; \
setenv FOO $BAR \; \
send-keys -t 0 "export FOO="$BAR C-m \; \
split-window -v \; \
send-keys -t 0 'echo $FOO' C-m \; \
send-keys -t 1 'echo $FOO' C-m
You can access tmux (local) environment variables for each session, while in a session, with the command:
bash> tmux show-environment
If you add the -g parameter you get the environment for all sessions, i.e. the global environment. The local environments are NOT the same as the global environment. The previous command prints the entire local environment, but you can also look at just one variable:
bash> tmux show-environment variable_name
variable_name=value
To get the value, you could use some 'sed' magic or use 'export' on a single variable, or you can even 'export' the entire environment to your shell. Below are the 3 approaches.
bash> tmux show-environment variable_name | sed "s:^.*=::"
value
bash> eval "export $(tmux show-environment variable_name)"
bash> echo $variable_name
value
bash> for var in $(tmux show-environment | grep -v "^-"); do eval "export $var"; done;
bash> echo $variable_name
value
If needed, you can just add the -g parameter after the show-environment command if you want to access the global environment.
I've done a simple
export MY_VAR="some value"
before I start the tmux session, which gives me access to MY_VAR from all windows inside that session.
Version 3.2 of tmux will support a -e option for the new-session command for altering the local environment directly.
In the mean time, you can use this to run a new tmux session with a particular environment available and up to date (or one of the other solutions mentioned):
tmux new-session 'export MY_VAR=value; exec bash'
The problem with tmux is that when you first run it, the tmux server is created and it inherits the environment that is available at this time of creation. This is called the global environment. When you run tmux again and create a new session, the tmux server still holds its copy of the environment. But this copy is the "old" environment that the server learned about when it was created, i.e. the global environment.
About the tmux commands that work with the tmux environment:
set update-environment MY_VAR: this will tell tmux to take the MY_VAR variable from the global environment and make it a session environment, i.e. a local environment (listed by the show-environment command). Then, the session will be able to change the value of this variable without affecting the global environment.
set-environment MY_VAR value: This will create (or change) a variable in the local environment of the session.
set-environment -g MY_VAR value: This will create (or change) a variable in the global environment.
Also, note that when you run tmux and tmux calls bash (or another shell), bash will have a copy of the server's (global) environment. Even if you change an environment variable before calling tmux, creating a new session will be oblivious of the changes. If you add a tmux command in a conf file and run tmux e.g. with tmux source-file tmux-session.conf, any variable you mention in the conf will be evaluated in the context of the global environments the tmux server knows about and not the in the context from where you run the tmux command. So, in order to make a new session see the new value of the variable, you have to call tmux in a way that passes the new value of the env variable directly to it. This is what the solutions here try to do. This is also what the new -e option will allow you to do.
I approached this a little differently, assuming I had separate initialization scripts for each environment.
Using tmux v3.0a.
The spec:
When I invoke tmux I want it to load up a specific environment for that session.
For each window within the tmux session I want that same environment session replicated.
I also want to be able to invoke subsequent tmux sessions with different environments and each window invocation should load that environment. (just like the first session) And subsequent sessions should not interfere with prior sessions.
I have shell scripts to initialize each of my environments.
So how can I associate a shell script with a session and have all window invocations use that shell script?
Simple Solution:
Found a simple solution here: How to start two tmux sessions with different environments?
With my original solution I ran into an issue with the tmux global environment. When I ran multiple sessions, whichever one ran first set the global environment. Then I would need to close other tmux sessions before I ran new sessions.
A better solution is to set up a separate tmux server for each environment.
Use the -L flag to tmux from within a shell that already has the environment defined you want.
Shell environment A: tmux -L environA
Shell environment B: tmux -L environB
Name your environments any way you want. Set up some aliases for each environment? Or you can test environment variables to determine which tmux server you want to use. Each tmux named server will keep its environment separate.
Then there is no need to set bash startup files, etc. as I had described in my original solution.
Example
In my case I'm starting a tmux session per project within a bash script and I needed to activate a relevant virtual environment for that session, including any newly opened tabs. To do so, I added the following virtual environment activation code to the ~/.bashrc:
if [ -n "$VIRTUAL_ENV" ]; then
source $VIRTUAL_ENV/bin/activate;
fi
However if I need to set foo environment for Session_1 and bar environment for Session_2, the VIRTUAL_ENV variable is globally set to bar after creating Session_2, so any newly opened tabs in Session_1 erroneously ends up in bar environment.
Solution
original HOWTO (commit).
Add the following in your ~/.profile (or ~/.bashrc):
# For Tmux VirtualEnv support
tmux_get_var(){
local key=$1
[[ -n "$TMUX" ]] && tmux showenv | awk -F= -v key="$key" '$1==key {print $2}'
}
# activate the virtual environment if it is declared
venv=$(tmux_get_var "VIRTUAL_ENV")
if [ -n "$venv" ]; then
source $venv/bin/activate;
fi
Set the VIRTUAL_ENV variable for the target session:
tmux setenv -t ${SESSION_NAME} 'VIRTUAL_ENV' /path/to/virtualenv/
Setting it for current session is quite easy: Ctr-b: to open tmux command prompt and enter setenv FOO foo. This will not apply for existing windows -- but there you can use export $(tmux show-env FOO).
For new sessions this pattern might be useful:
tmux new -s session
Ctr-b : set-env FOO foo -- set FOO=foo for the session
Ctr-b c -- create new, second window ($FOO is set here)
Ctr-b l -- go to old, first window
exit the first window
all windows now have FOO=foo
Tested with Tmux 1.8.
I'm trying to move from screen to tmux (to eventually using tmux from within byobu). However, I have a severe problem with environment variables not being applied, especially PS1. None of them get copied to the session (or window) environments. I found this thread that seemed relevant:
How do I start tmux with my current environment?
However, I actually can see all my right variables when I do
:show-environment -g
But none of them get carried over to the session environment, so I don't see anything when I do
:show-environment
I do have the right update-environment statement in my ~/.tmuxrc file:
# start a non-login shell by default for each new window
set -g default-command 'bash'
# Prefix is C-a like in screen
unbind C-b
set -g prefix C-a
# Carry over the PS1
set-option -ga update-environment "PS1"
Frankly this all seems like a mess to me. I can see the benefit of starting from a clean session for each screen, but for the most part this seems like a pain. I export the variable I intend to use in sub-processes, such as as the Python virtualenvwrapper functions, and expect them to be available.
Is there a way to disable this behavior? If not, what should I try to carry over my PS1 variable to tmux?
EDIT 11/13/2013
I realized that removing the first line (default-command bash) does carry over all of the environment variables. However, I really don't want each new screen of tmux to launch as a login shell. For instance, I specifically declared my PS1 variable in a login shell, so that it wouldn't be overwritten whenever I open a new screen.
Based on the following post, every new screen in tmux should launch as a non-login shell:
https://superuser.com/questions/614277/profile-and-bash-profile-ignored-when-starting-tmux-from-bashrc
Why is this not happening by default for me?
Tmux cannot update running processes (e.g. bash), it can only update its own environment. If you were to start a new window/pane it would pick up the new environment. My suggestion would be to use a utility function like this:
#!/bin/bash
tmup ()
{
echo -n "Updating to latest tmux environment...";
export IFS=",";
for line in $(tmux showenv -t $(tmux display -p "#S") | tr "\n" ",");
do
if [[ $line == -* ]]; then
unset $(echo $line | cut -c2-);
else
export $line;
fi;
done;
unset IFS;
echo "Done"
}
This goes through all the updated environment that tmux has learned about and updates your shell.
I have this problem with tmux 1.8: I want to set status-keys option to 'emacs' because I really dislike entering commands in vi-mode. However adding the following line to .tmux.conf has no effect:
set -g status-keys emacs
When tmux is restarted, tmux show-options -g | grep keys says emacs but the actual behaviour is vi-style.
The root of the problem is the $EDITOR environment variable, which it set to vim in my case. The documentations states:
status-keys [vi | emacs]
Use vi or emacs-style key bindings in the status line,
for example at the command prompt. The default is emacs,
unless the VISUAL or EDITOR environment variables are set
and contain the string `vi'.
So apparently when the environment variable is "vim" it forces vi status-keys.
Is there a way to override this behaviour and have the prompt behave emacs-style despite the environment variable? I can obviously hack around this (like starting tmux with other env variables and restoring the original later) but I hope there is a clean solution.
Thanks!
I had this problem and I think I just figured it out. Are you by chance also using ZSH (Z Shell)?
I found this post that says that ZSH will also switch to "vi mode" if your VISUAL and/or EDITOR is set to vi/vim. So the problem I was having in tmux as actually bubbling up from ZSH!
In short, make sure you can use emacs-style keys in your shell outside of tmux. If you're using ZSH you can add bindkey -e to .zshrc to set emacs bindings. Then in .tmux.conf:
set -g mode-keys emacs
set -g status-keys emacs
Both status-key and mode-keys default to vi if EDITOR contains vi, but this is only a default (during startup, before the configuration files are processed). Setting either of these options in your configuration should override the EDITOR-based default.
Are you also setting mode-keys (which controls the key maps used in copy-mode, and the choose-… commands)?
set -gw mode-keys emacs
I wonder is there any easy way that I can change an bash environment variable and apply to windows in a tmux session?
I've googled and didn't found a solution so I post the question here.
Thanks!!
You can't directly change the environment variables.
However, you can make shell terminals inside a tmux instance pick up new variables. This requires a few steps.
First, tmux can update environment variables in itself. The update-environment configuration directive does this. Any variable listed here will be auto-udpated - connecting to a tmux session updates the session's environment variable with the value from the client you just connected. So if you run tmux attach, and have SSH_AUTH_SOCK set, then the tmux session will get the new value of SSH_AUTH_SOCK.
However, that only updates the session's variables, so only new panes/windows will get the values. Existing windows will still have the old values. As #IgnacioVazquez-Abrams said, there is not a direct fix to this.
However, you can fix it in many programs. Sufficiently recent versions of tmux have a show-environment command, which queries the session for the current values of an environment variable. This allows you to get the environment variable from within a program after it has been with the update-environment logic.
For instance, you can have some shells pick up the new value. In ZSH, I do this with a precmd hook in ~/.zshrc:
_update_ssh_agent() {
local var
var=$(tmux show-environment |grep '^SSH_AUTH_SOCK=')
if [ "$?" -eq 0 ]; then
eval "$var"
fi
}
if [[ -n "$TMUX" ]]; then
add-zsh-hook precmd _update_ssh_agent
fi
This makes it so that shells inside tmux sessions update the SSH agent every time they go to display a new prompt. After I reconnect, all I have to do is hit enter at a prompt and it grabs the new SSH agent connection.
Bash also has a pre-command mechanism, although I do not know how to configure it off-hand. You could additionally configure more programs to use show-environment to update themselves, such as vim or emacs. However, you will need to configure each program you might want to do this with separately.
No.
And there isn't even a hard way; manipulating the environment variables of another process is non-portable at best.
So this is an old question, but came up when I was searching for what I wanted to do, so here's another solution.
When tmux is started, it copies the environment into a global environment. When new windows are created, it merges the global environment with the session environment to set the environment for that window. We can view these environments with:
tmux show-environment [-g] [-t target-session]
Even better, we can also update the environments, even from within the windows' own tmux session:
tmux set-environment [-gru] [-t target-session] name [value]
For a full explanation of the flags, see the tmux man page.
This feature was exactly what I wanted. Unfortunately it won't update the environment of existing shells, but we could easily loop over tmux showenv -g to re-export the tmux environment that might be set by another process.
Here was my use-case:
With the way I have my shell configuration set up, I export my environment in a login shell, and then exec into a tmux session. I figure since env vars don't change often, I don't need to source them in new non-login shells every time. However, when they do change, I don't want to restart my entire tmux session to have new tmux windows know about the change. While using the update-environment option and re-attaching solves a session restart, it's cumbersome to maintain an explicit list of potentially malleable env vars in my tmux configuration.
With the following function, we can update our environment variables in something like ~/.shenv for example, and instead of sourcing the file (. ~/.shenv), we can promote the file (promote ~/.shenv) for new tmux windows to inherit from this updated environment.
promote() {
sourced="$1"; shift
. "$sourced"
env - \
"HOME=$HOME" \
sh -c ". $sourced; env" | cut -d= -f1 | while read -r var; do
val="$(eval "echo \"\$$var\"")"
tmux set-environment -gt local "$var" "$val"
done
}
What's happening here is we're sourcing our file from within a shell with an empty environment (apart from the HOME we add), and print out our new env vars. We pipe those into a cut to extract the variable of each line, storing it in a variable called var. After some meta magic with eval to get the indirect value of that variable from the current shell, we update our tmux target session. In this case, local is the name of our tmux session.
After wrestling with different solutions, I came up with this bash function added to my .bashrc file:
if [[ -n $TMUX ]]; then
_fix_ssh_agent_in_tmux () { if [[ ! -S $SSH_AUTH_SOCK ]]; then eval export $(tmux show-env | grep SSH_AUTH_SOCK); fi }
ssh () { _fix_ssh_agent_in_tmux; command ssh $#; }
scp () { _fix_ssh_agent_in_tmux; command scp $#; }
rsync () { _fix_ssh_agent_in_tmux; command rsync $#; }
fi
If the shell is running within tmux, it redefines 'ssh' to a bash function that tests and fixes SSH_AUTH_SOCK before actually running the real ssh command.
Note that tmux show-env -g also returns a value for SSH_AUTH_SOCK but that one is stale, I assume it's from whenever the tmux server started. The command above queries the current tmux session' environment which seems to be correct.
I'm using tmux 2.6 (ships with with Ubuntu 18.04) and it seems to work well.