You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
pyenv init getting into an infinite loop when invoked from bashrc #264
The current installation instructions suggest to place the line
eval "$(pyenv init -)"
at the end of .bash_profile or .bashrc depending on the Linux flavor.
This suggestion may lead to the following scenario:
The user puts it into .bashrc because that's where he is used to placing bash environment configuration.
The corresponding Linux flavor has bash configured to execute .bashrc on each bash invocation (not sure about Ubuntus, but this seems to be normal behaviour for many Linux flavors).
On login, the .bashrc is executed
The code in .bashrc reaches "pyenv init"
pyenv is an executable script which has #!/usr/bin/env bash specified as the interpreter
Consequently, bash is fired up to execute the script.
The new bash executes .bashrc
GOTO 4
Obviously, the immediate fix for the problem is to put the initialization line into .bash_profile rather than .bashrc, however at the point in time, when the user encounters this error, this is far from obvious (in particular, this has been the reason for me to not start using pyenv the first time I tried it and had no time to debug the problem, and now it took quite some time to figure out what's happening).
It would be great to find some nice way to prevent this possibility for an infinite loop somehow. The options I'd see, in order of niceness:
Get rid of the need to run a script on initialization completely. Once I'm adding the $PYENV_ROOT/bin to the path manually on installation, I could as well add the $PYENV_ROOT/shims there in the same command line.
Have #!/usr/bin/env sh in the script header (this way bash won't load bashrc) and make sure nothing breaks.
If nothing else, at least fix the current documentation to mention the possibility of this caveat in red blinking letters somewhere.
It is pretty weird. I couldn't reproduce such strange behaviour at least on Debian/Ubuntu/OS X. The ~/.bashrc must not be read from non-interactive shell and it should not causes infinite loops.
You can use pyenv without invoking pyenv init -. Only few of pyenv commands (e.g. pyenv shell) require pyenv init -.
pyenv is written in bash and cannot run with standard POSIX sh
It would be better to be documented if there is possibility of infinite loops. But first, we need to clarify the problem. Please describe your platform and shell information (version, configurations, etc.)
The more reasons there are to consider a refactoring where there is no need to use pyenv init at all.
Yes, I was suspecting that, there was a vague hope the required changes would not be too drastic.
This is not an out-of-the-box behaviour, but it is enabled by setting BASH_ENV=~/.bashrc, which is a fairly common configuration practice (I've seen many systems where this has been enabled by default, perhaps because this is a kind of a textbook configuration example). My example stems from a CentOS system and I suspect I was not the one to set the BASH_ENV in my .bash_profile.
I confirmed that this doesn't reproduce with out-of-box Debian/Ubuntu. I created a Docker container for reproduction, but the .bashrc bundled with those distributions will just return if $PS1 is not defined (=~ non-interactive).
% docker run -it yyuu/pyenv:issue264 bash -l
this is .bashrc
this is .profile
root@ddbcabcb80ef:/#
I am wondering if I should fix this in pyenv script. This is caused by a kind miss configuration of the system, I think. Though, this is pretty easy to reproduce. Mentioning about potentially infinite loop in Wiki or README might be sufficient.
This is the Dockerfile. (you can also pull the image from DockerHub)
FROM ubuntu:14.04
MAINTAINER Yamashita, Yuu <peek824545201@gmail.com>
ENV PATH /root/.pyenv/shims:/root/.pyenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get --quiet --yes update
RUN apt-get --quiet --yes upgrade
RUN apt-get --quiet --yes install build-essential curl git libbz2-dev libreadline-dev libsqlite3-dev libssl-dev patch zlib1g-dev
RUN git clone --quiet https://github.com/yyuu/pyenv.git /root/.pyenv
RUN cd /root/.pyenv && git reset --hard 35aed21
RUN echo'export BASH_ENV="/root/.bashrc"'>> /etc/profile
RUN echo'echo "this is .profile"'>> /root/.profile
RUN echo'echo "this is .bashrc"'>> /root/.bashrc
RUN echo'eval "$(pyenv init -)"'>> /root/.bashrc
## Enable infinite loop#RUN sed -i.orig -e '/^\[ -z "\$PS1" \]/s/^/#/' /root/.bashrc
As I mentioned, I encountered this in CentOS rather than Debian. Apparently Debian/Ubuntu has the concept of only having .bashrc which has a hard-coded check for non-interactive shell in the beginning, whereas CentOS/RedHat has both a .bash_profile (which is run for login shells) and a .bashrc (which is meant to be run for non-interactive shells). In particular bash_profile sets BASH_ENV to point to .bashrc.
The most easy way to reproduce the infinite loop is to change the line
RUN echo 'eval "$(pyenv init -)"' >> /root/.bashrc
to
RUN echo 'eval "$(pyenv init -)"' > /root/.bashrc
in your Dockerfile, and then, for example, invoke pyenv.
Yes, I think that this should be mentioned in the docs somewhere next to the installation instructions that require adding $(pyenv init -) to the startup script.
@StevenACoffman's solution didn't quite work for me on zsh, so I implemented a workaround using environment variables here:
# Don't initialize pyenv if it is already initialized.
# See: https://github.com/pyenv/pyenv/issues/264#issuecomment-358490657
if [ -n "$PYENV_LOADING" ]; then
true
else
if which pyenv > /dev/null 2>&1; then
export PYENV_LOADING="true"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
unset PYENV_LOADING
fi
fi
Reacted by fiskhest, Tobias McVey, candito123, Malcolm Jones (bossjones/Tony Dark) and EarningsCall
Line 1 prevents the file from being processed any further if we're executing non-interactively (ie. triggering bash via the pyenv framework itself). This is the method used by many default .bashrc files.
If, like me, you're on a bash from Debian Buster or newer and have never even heard of the above BASH_ENV weirdness, let alone used it, then maybe you too have been bitten by bash's attempt to determine whether it's been run under an SSH_CLIENT and, if so, to automatically source .bashrc when you didn't want it, eg in pyenv-init, per https://stackoverflow.com/a/58860714/18096. Adding --norc to that script solves that case, making pyenv work out of the box on Debian with the recommended configuration once again, for the first time since Stretch:
jenkins@automation-d10-1:~$ diff -u /var/lib/jenkins/.pyenv/libexec/pyenv-init{.orig,}
--- /var/lib/jenkins/.pyenv/libexec/pyenv-init.orig 2022-05-18 01:31:09.503857571 +0100
+++ /var/lib/jenkins/.pyenv/libexec/pyenv-init 2022-05-18 01:29:54.702473361 +0100
@@ -148,7 +148,7 @@
echo 'set -gx PATH '\'"${PYENV_ROOT}/shims"\'' $PATH'
;;
* )
- echo 'PATH="$(bash -ec '\''IFS=:; paths=($PATH); for i in ${!paths[@]}; do if [[ ${paths[i]} == "'\'"${PYENV_ROOT}/shims"\''" ]]; then unset '\'\\\'\''paths[i]'\'\\\'\''; fi; done; echo "${paths[*]}"'\'')"'
+ echo 'PATH="$(bash --norc -ec '\''IFS=:; paths=($PATH); for i in ${!paths[@]}; do if [[ ${paths[i]} == "'\'"${PYENV_ROOT}/shims"\''" ]]; then unset '\'\\\'\''paths[i]'\'\\\'\''; fi; done; echo "${paths[*]}"'\'')"'
echo 'export PATH="'"${PYENV_ROOT}"'/shims:${PATH}"'
;;
esac
jenkins@automation-d10-1:~$
That'd be harmless, wouldn't it? The recommendation that was made in the pyenv documentation under this, ahem, long Closed issue, on the other hand, recommends something that isn't going to work for the invocation I'm seeing, from Jenkins rather than ssh.
I can't be forking pyenv-init locally. What happens when you return inside a script that's been sourced, potentially from within a function?
mad@shuttle:~$ cat scr
echo got here
return
echo and here
mad@shuttle:~$ srcscr() { . scr; echo and also here; }
mad@shuttle:~$ srcscr
got here
and also here
mad@shuttle:~$
Oh, just what we want. That's OK then. I'd rather limit the behavior to the pyenv part of .bashrc and leave a code-comment about the departure from upstream's (current) documentation:
jenkins@automation-d10-1:~$ cat .bashrc
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
# pyenv-init can cause .bashrc to be sourced under SSH_CLIENT
[ -z "$PS1" ] ||
eval "$(pyenv init -)"
jenkins@automation-d10-1:~$
... and hope someone takes pity on me, reopens this and makes pyenv-init cope with bash's weirdness... or changes the documentation.
Doh, that PS1 test doesn't work for my jenkins use-case - the whole reason I needed the pyenv stanza in .bashrc is because it's being run from a non-interactive shell. A copy-and-paste solution for that would have helped me:
jenkins@automation-d10-1:~$ cat /var/lib/jenkins/.bashrc
if [ -z "$PYENV_ROOT" ]
then
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
fi
jenkins@automation-d10-1:~$
Activity
yyuu commentedon Nov 2, 2014
It is pretty weird. I couldn't reproduce such strange behaviour at least on Debian/Ubuntu/OS X. The
~/.bashrcmust not be read from non-interactive shell and it should not causes infinite loops.http://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
The followings are my answers:
pyenv init -. Only few of pyenv commands (e.g.pyenv shell) requirepyenv init -.bashand cannot run with standard POSIXshkonstantint commentedon Nov 2, 2014
yyuu commentedon Nov 4, 2014
I confirmed that this doesn't reproduce with out-of-box Debian/Ubuntu. I created a Docker container for reproduction, but the
.bashrcbundled with those distributions will just return if$PS1is not defined (=~ non-interactive).% docker run -it yyuu/pyenv:issue264 bash -l this is .bashrc this is .profile root@ddbcabcb80ef:/#I am wondering if I should fix this in
pyenvscript. This is caused by a kind miss configuration of the system, I think. Though, this is pretty easy to reproduce. Mentioning about potentially infinite loop in Wiki or README might be sufficient.This is the
Dockerfile. (you can also pull the image from DockerHub)konstantint commentedon Nov 4, 2014
As I mentioned, I encountered this in CentOS rather than Debian. Apparently Debian/Ubuntu has the concept of only having .bashrc which has a hard-coded check for non-interactive shell in the beginning, whereas CentOS/RedHat has both a .bash_profile (which is run for login shells) and a .bashrc (which is meant to be run for non-interactive shells). In particular bash_profile sets BASH_ENV to point to .bashrc.
The most easy way to reproduce the infinite loop is to change the line
to
in your Dockerfile, and then, for example, invoke pyenv.
Yes, I think that this should be mentioned in the docs somewhere next to the installation instructions that require adding $(pyenv init -) to the startup script.
yyuu commentedon Nov 4, 2014
山下 優 (やました ゆう)
peek824545201@gmail.com
Warn about pyenv#264 in the README
Merge pull request #266 from konstantint/patch-1
yyuu commentedon Nov 29, 2014
Adding warning in README has already been merged.
StevenACoffman commentedon Mar 2, 2017
Might be of value to others, but I conditionally initialized pyenv to avoid this problem.
jfly commentedon Jan 17, 2018
@StevenACoffman's solution didn't quite work for me on zsh, so I implemented a workaround using environment variables here:
t3hpr1m3 commentedon Jul 7, 2020
A little late to the party, but a more idiomatic fix I'm using is:
Line 1 prevents the file from being processed any further if we're executing non-interactively (ie. triggering bash via the pyenv framework itself). This is the method used by many default
.bashrcfiles.A-R-M commentedon Jul 17, 2020
Slight issue with the first line, it should be:
instead (with a space between the
[and the-z). Otherwise,bashwill give a[-z: command not founderror.martindorey commentedon May 18, 2022
If, like me, you're on a bash from Debian Buster or newer and have never even heard of the above BASH_ENV weirdness, let alone used it, then maybe you too have been bitten by bash's attempt to determine whether it's been run under an
SSH_CLIENTand, if so, to automatically source.bashrcwhen you didn't want it, eg inpyenv-init, per https://stackoverflow.com/a/58860714/18096. Adding--norcto that script solves that case, making pyenv work out of the box on Debian with the recommended configuration once again, for the first time since Stretch:That'd be harmless, wouldn't it? The recommendation that was made in the pyenv documentation under this, ahem, long Closed issue, on the other hand, recommends something that isn't going to work for the invocation I'm seeing, from Jenkins rather than ssh.
I can't be forking pyenv-init locally. What happens when you
returninside a script that's beensourced, potentially from within a function?Oh, just what we want. That's OK then. I'd rather limit the behavior to the pyenv part of
.bashrcand leave a code-comment about the departure from upstream's (current) documentation:... and hope someone takes pity on me, reopens this and makes pyenv-init cope with bash's weirdness... or changes the documentation.
martindorey commentedon May 18, 2022
Doh, that
PS1test doesn't work for my jenkins use-case - the whole reason I needed the pyenv stanza in.bashrcis because it's being run from a non-interactive shell. A copy-and-paste solution for that would have helped me: