2

I have a script at /venv/bin/activate that sets some environment variables. I’m looking for a way to start zsh, have it source this script, and then continue with a normal interactive session. When I exit this session, the session I return to should not know about any of the environment changes that activate made. How can I invoke zsh like this?

(This is pretty much the same question as this one, except that I’m using zsh instead of bash.)

bdesham
  • 1,327
  • 2
  • 12
  • 22

2 Answers2

2

If the activate script only sets environment variables, you can read it in one shell, then execute a new, interactive zsh instance.

sh -c '. /venv/bin/activate; exec zsh -i'

The wrapper shell sh can be replaced by any shell that is able to parse /venv/bin/activate, including zsh if the activate script is compatible with it.

In addition to environment variables, the interactive zsh instance will inherit the process ID and a few other process settings such as resource limits (ulimit …) and ignored signals (trap '' …). On the other hand, settings of the shell itself are not preserved: shell variables (var=… if var is not exported), shell options (set -… or shopt …), key bindings, alias and function definitions, etc.

Obviously this won't work if your .zshrc overrides environment variables set by the activate script. That's one of the reasons .*shrc files should not set environment variables.

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
  • This is a good solution as long as you don’t need to return to the original shell afterward (and as long as the effects of `activate` fall into only the categories you mentioned). – bdesham Sep 06 '19 at 20:45
  • @bdesham Yes, I meant that you should run that as a separate shell script but I can see that it isn't clear, I'll clarify. – Gilles 'SO- stop being evil' Sep 06 '19 at 20:56
  • Oh, even better. Thanks for clarifying. – bdesham Sep 06 '19 at 21:40
  • This doesn't work with modern zsh (nb it works if the final shell command is `sh` or `bash`, for example) – jmetz Mar 12 '21 at 16:17
  • @jmetz What doesn't work? It certainly works with Python virtual environments, and I don't see what recent changes in zsh could break similar scripts from other communities. The only thing I can see that would break it is if you do something weird in your `.zshrc` like overriding `PATH`. In which case, don't do this. – Gilles 'SO- stop being evil' Mar 12 '21 at 17:34
  • @Gilles'SO-stopbeingevil' - overriding `PATH` is indeed the culprit then it seems... though I'd have though that this is fairly standard practice meaning your answer is only useful in limited scenarios. Or have I been overriding `PATH` in the wrong place..? – jmetz Mar 12 '21 at 17:49
  • Regardless, I'd suggest adding an emphasized note to your answer highlighting the `PATH` issue, as I doubt I'd be the only person left scratching my head after trying your answer. – jmetz Mar 12 '21 at 17:54
  • @jmetz No, [setting environment variables in `.zshrc` or `.bashrc` is bad practice](https://unix.stackexchange.com/questions/107851/using-export-in-bashrc/197333#197333), in part because it breaks per-session environments as you noticed. – Gilles 'SO- stop being evil' Mar 12 '21 at 18:38
  • @jmetz I don't really see what to add to my answer. Your problem has nothing to do with this question: it's independent of the shell, whereas the question is about using a sh script with zsh. – Gilles 'SO- stop being evil' Mar 12 '21 at 18:39
  • @Gilles'SO-stopbeingevil' Have you tried googling "setting PATH zsh"? Pretty much every result suggests setting `PATH` in `.zshrc`, so bad practice or no, your answer needs to take the fact that there are probably many `zshrc`s with `PATH` setting in them. – jmetz Mar 13 '21 at 12:47
  • @jmetz In my experience this bad recommendation almost always mentions `.bashrc` and is partly due to the bizarre treatment of shrc files by bash. But ok, I've added a warning not to do this. Maybe the bad practice has migrated to zsh with macOS? – Gilles 'SO- stop being evil' Mar 13 '21 at 13:06
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/120831/discussion-between-jmetz-and-gilles-so-stop-being-evil). – jmetz Mar 13 '21 at 18:29
1

Using ZDOTDIR

Create a (possibly temporary) directory at, say, ~/venv_startup that contains two files, .zshenv and .zshrc. The .zshenv file consists of

source ~/.zshenv
source /venv/bin/activate

while .zshrc says

source ~/.zshrc

Now, invoke zsh via

zsh -c "ZDOTDIR=~/venv_startup zsh"

This says to zsh, “instead of starting out by sourcing the .zshenv and .zshrc files in $HOME, source the files of those names in /venv_startup instead.” Since the versions in /venv_startup source the ones in $HOME anyway, the net effect is that the shell will run

source /venv/bin/activate

in between executing your zshenv and your zshrc. After that, it will be a normal interactive session.

Feline
  • 138
  • 5
bdesham
  • 1,327
  • 2
  • 12
  • 22