Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

.ruby-version auto-switch ignored within subshells #241

aprescott opened this Issue Jan 21, 2014 · 11 comments


None yet
4 participants

I'm not sure if this is the same as / similar to #168, but subshells appear to not use auto-switching.

~$ cd /tmp
/tmp$ chruby
 * ruby-2.0.0-p353

/tmp$ mkdir foo; echo 2.0.0-p353 > foo/.ruby-version
/tmp$ mkdir bar; echo 2.1.0 > bar/.ruby-version

/tmp$ cd foo/
/tmp/foo$ chruby
 * ruby-2.0.0-p353    # correct

/tmp/foo$ cd ../bar/
/tmp/bar$ chruby
 * ruby-2.1.0         # correct

/tmp/bar$ (cd ../foo && chruby)
 * ruby-2.1.0         # wrong
$ bash --version
GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
Copyright (C) 2007 Free Software Foundation, Inc.

OS X 10.9, chruby 0.3.8.


mpapis commented Jan 22, 2014

from man bash:

  set [--abefhkmnptuvxBCEHPT] [-o option-name] [arg ...]
  set [+abefhkmnptuvxBCEHPT] [+o option-name] [arg ...]

-T If set, any traps on DEBUG and RETURN are inherited by shell functions, command substitutions, and commands executed in a subshell environment. The DEBUG and RETURN traps are nor-
mally not inherited in such cases.

I would try set -T around: https://github.com/postmodern/chruby/blob/master/share/chruby/auto.sh#L30


postmodern commented Jan 22, 2014

Any objections to enabling set -T by default?

I was going to say no objections, but I just tried this and it's messing with my PS1 for some reason.

Narrowed down the problem: I have a DEBUG trap in my .bashrc as per #227. So the end of my auto.sh is actually:

function chruby_trap() {
    [[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto

if [[ -n "$ZSH_VERSION" ]]; then
    if [[ ! "$preexec_functions" == *chruby_auto* ]]; then
elif [[ -n "$BASH_VERSION" ]]; then
    set -T
    trap chruby_trap DEBUG

This is almost identical to the existing auto.sh provided by chruby but I expose chruby_trap. Then my .bashrc uses this to reset colouring:

# Reset color for command output and call chruby's trap function
trap 'echo -ne "\033[0m" && chruby_trap' DEBUG

Since my PS1 uses __git_ps1

PS1="some stuff \$(__git_ps1 '::%s') other stuff"

the set -T forces echo -ne to fire since now the DEBUG trap is inherited. The end result being that a PS1 of

PS1="abcdefghijklmnop\$(__git_ps1 '::%s')1234567890"

will cause a new terminal to appear as


where [k] indicates the cursor is over the k character, and typing will type over the klmnop portion.

So basically:

  • __git_ps1 in PS1 with other colour codes.
  • Existing DEBUG trap outside of chruby which inserts \033[0m to reset colouring for stdout.
  • Inheriting trap causes existing DEBUG to fire when __git_ps1 is executed.

I suppose an argument is that this is simply not something chruby can work around and my desire to have certain colouring in my PS1 + reset colouring for output through echo -ne "\033[0m" in a DEBUG trap, but it would be nice to have them coexist.

I suppose there might be a way to call set +T and then set -T around my PS1 to bypass it for the __git_ps1 call?


mpapis commented Jan 22, 2014

you could try to use a wrapper that checks what function is called and do reset color only in some cases, or ignoring the __git_ps1 function:

[[ " ${FUNCNAME[*]} " == *" __git_ps1 "* ]]

@mpapis that's awesome, thanks!

I'm 👍 on set -T, even before I had that workaround, since I don't think this will apply to many people.

set -T seems to only fix Bash. I have a test in my fork's branch that fails for zsh:

SHELL=`which bash` ./test/runner
�>>> Running tests under /bin/bash in interactive-mode ...�
>>>� Running ./test/chruby_auto_test.sh ...�

Ran 13 tests.

SHELL=`which zsh`  ./test/runner
>>> Running tests under /usr/bin/zsh in interactive-mode ...
>>> Running ./test/chruby_auto_test.sh ...
ASSERT:did not switch Ruby when subshell entered versioned directory expected:</home/travis/build/aprescott/chruby/test/rubies/ruby-1.9.3-p429> but was:<>

Ran 13 tests.
FAILED (failures=1)

I don't know zsh enough to get this passing, but if someone lets me know then I can update my branch and PR a fix for this bug.

aprescott added a commit to aprescott/chruby that referenced this issue Jan 29, 2014


mpapis commented Jan 29, 2014

that might be unsetopt localtraps try:

if [[ -n "${ZSH_VERSION:-}" ]]
then unsetopt localtraps
else set -T

an important point about Zsh is that it has to be run in top scope, not in a function that gets called from shell

aprescott referenced this issue in aprescott/chruby Jan 30, 2014

Use chpwd instead of preexec.
Allows 'cd foo && cmd' to see .ruby-version, as well as in subshells for

Fixes postmodern/chruby#245.

postmodern commented Jan 30, 2014

After doing some testing, the trap DEBUG and preexec_functions hook are being inherited by the sub-shell.


$ trap -p DEBUG
trap -- '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG
$ (trap -p DEBUG)
trap -- '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG


% eval 'echo ${preexec_functions[@]}' 
% (eval 'echo ${preexec_functions[@]}')

I believe the reason why the chruby_auto function isn't working, is because the sub-shell command is being treated as one single command, not individual commands which chruby_auto runs before.

FWIW adding set -T before line 30 in auto.sh fixed my non-functioning auto-switching problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment