In what order does rbenv decide which Ruby version is active, and how do rbenv shell, local and global map onto that order?
answer
- variable beats file beats default
- RBENV_VERSION set by rbenv shell
- .ruby-version searched upward
- ~/.rbenv/version from rbenv global
- no prefix matching on names
basics
~20 srbenv takes RBENV_VERSION first (set by rbenv shell), then the nearest .ruby-version found walking up from the current directory (written by rbenv local), then ~/.rbenv/version (written by rbenv global), and otherwise uses the system Ruby.
solid answer
~30 sAt every shim call rbenv resolves the version from the first source that yields one: the `RBENV_VERSION` variable, which `rbenv shell` sets; the nearest non-empty `.ruby-version`, searched from the current directory up to `/`, which `rbenv local` writes; `~/.rbenv/version`, which `rbenv global` writes; and finally `system`. Only the first word of the file counts, a leading `ruby-` is tolerated, and names match installed directories exactly, so `4.0` does not pick up `4.0.7`. `rbenv version` prints the active name and the file or variable that set it, which is the first thing I run when the wrong Ruby shows up.
code
bash · 9 linesrbenv global 3.4.7
cd ~/code/invoice-api && rbenv local 4.0.7
rbenv version # 4.0.7 (set by /home/dev/code/invoice-api/.ruby-version)
rbenv shell 3.4.7
rbenv version # 3.4.7 (set by RBENV_VERSION environment variable)
rbenv shell --unset
echo 4.0 > .ruby-version
ruby -v # rbenv: version `4.0' is not installed (set by .../.ruby-version)
rbenv local 4.0.7 # back to an exact, installed namego deeper
Recall the order: RBENV_VERSION, then .ruby-version, then ~/.rbenv/version, then system, and which command writes each one.
Explain the upward file search, the first-word read, the ruby- prefix tolerance, the exact-name rule and why rbenv shell needs a shell function.
Use rbenv version and its origin text to diagnose wrong-Ruby reports quickly, and spot inherited RBENV_VERSION or a stray home-directory .ruby-version.
Decide what a team commits: exact patch versions in .ruby-version for reproducibility versus aliases that ease upgrades but hide which Ruby CI really ran.
## The four sources, in order Every time a shim runs, rbenv answers one question: **which version name applies right now?** The answer comes from the first of four sources that yields a name: 1. **`RBENV_VERSION`** environment variable. `rbenv shell <version>` sets it; you can also export it yourself. 2. The nearest **`.ruby-version`** file, searched from the current directory upward to the filesystem root. `rbenv local <version>` writes one. 3. The **global version file** `~/.rbenv/version` (strictly `$RBENV_ROOT/version`). `rbenv global <version>` writes it. 4. Nothing found: the name is **`system`**, the Ruby found on `PATH` without rbenv's shims. | Command | Where it records the choice | Scope | Precedence | |---|---|---|---| | `rbenv shell 4.0.7` | `RBENV_VERSION` in the current shell | One shell session | Highest | | `rbenv local 4.0.7` | `./.ruby-version` | A directory tree | Middle | | `rbenv global 4.0.7` | `~/.rbenv/version` | Whole user account | Lowest before `system` | So an exported `RBENV_VERSION` beats a project's `.ruby-version`, and a `.ruby-version` beats the global default. ## How the .ruby-version search works The lookup (`rbenv version-file`) is a simple upward walk: - It starts at `RBENV_DIR`, which defaults to the current directory. For the `ruby` shim, when an argument is a path to an existing file, `RBENV_DIR` becomes that file's directory, so `ruby ~/tools/report/run.rb` honours the tool's own `.ruby-version`. - At each level it checks for a **non-empty** `.ruby-version`; an empty file is skipped as if absent. - It reads only the **first word** of the file, so trailing comments or a Windows line ending do no harm. - A value of `..` or anything containing `/` is rejected with an "invalid version" message. - If no file is found, the global version file is used, even if it does not exist (then the name is `system`). A `.ruby-version` in your home directory therefore pins every project under it that has no file of its own. ## Exact names only rbenv matches the name against directories under `~/.rbenv/versions` **exactly**. Two tolerances exist: `system` is never checked for existence, and a leading `ruby-` is stripped when the literal name is not installed, so `ruby-4.0.7` works if `4.0.7` is installed. There is **no prefix matching**: a file saying `4.0` fails even when `4.0.7` is installed: ```bash $ ruby -v rbenv: version `4.0' is not installed (set by /home/dev/app/.ruby-version) ``` Fix the file, or create a symlink `~/.rbenv/versions/4.0 -> 4.0.7`; the README explicitly allows entries in that directory to be symlinks. Note that ruby-build's `rbenv install 4.0` does resolve prefixes, but it installs under the full name, so it does not make a `4.0` file valid. ## rbenv shell and shell integration `rbenv shell` has to change the **current** shell's environment, which a child process cannot do. `rbenv init -` therefore defines an `rbenv()` shell function that routes `shell` and `rehash` to their `sh-` variants and evaluates their output. Without that integration you can still export `RBENV_VERSION` by hand. Related forms: - `rbenv shell --unset` removes `RBENV_VERSION`, letting the file-based sources apply again. - `rbenv shell -` restores the value that was set before the last `rbenv shell` call. - `rbenv shell` with no argument prints the current `RBENV_VERSION`, or errors if none is set. ## Asking rbenv where the version came from - `rbenv version` prints the name and its origin, for example `4.0.7 (set by /home/dev/app/.ruby-version)` or `3.4.7 (set by RBENV_VERSION environment variable)`. - `rbenv version-name` prints only the name. - `rbenv version-file` prints the path of the file that would be used. - `rbenv versions` lists all installed versions with an asterisk on the active one. Reading the origin first turns most "wrong Ruby" reports into a one-line diagnosis: the output names the exact variable or file to change. Because rbenv starts its search at `RBENV_DIR`, a script can ask about another directory without changing into it: `RBENV_DIR=~/code/invoice-api rbenv version` resolves the version exactly as a command run inside that project would. ## Why the order is designed this way The most temporary setting wins: an environment variable for an experiment in one terminal, then a committed per-project file, then a personal default. Because the lookup runs at every shim call, moving between directories needs no hook: the next command simply finds a different file.
- A .ruby-version file says 4.0 and only 4.0.7 is installed under rbenv; what happens and how do you fix it?Every shim call fails with a 'version 4.0 is not installed' error naming that `.ruby-version`, because rbenv matches directory names exactly. Write `4.0.7` into the file with `rbenv local 4.0.7`, or, if you want a moving alias, create a symlink `~/.rbenv/versions/4.0` pointing at `4.0.7`; the versions directory accepts symlinks.
- Why can ruby path/to/script.rb, run from another directory, use the script's own .ruby-version under rbenv?The `ruby` shim scans its arguments before `-e` or `--`; when one contains a slash and names an existing file, it sets `RBENV_DIR` to that file's directory. The `.ruby-version` search then starts there instead of the current directory, so a tool's pinned version applies wherever you call it from.
- How do you undo rbenv shell 3.4.7 in the current shell?`rbenv shell --unset` removes `RBENV_VERSION`, so `.ruby-version` and the global file apply again; `rbenv shell -` instead restores whatever value `RBENV_VERSION` had before the last `rbenv shell` call. Both need rbenv's shell integration, because only a shell function can change the current shell's environment.
saying these in an interview costs you the question
- A project's .ruby-version beats RBENV_VERSION exported in the shell
- rbenv reads .ruby-version only from the current directory, never parents
- Writing 4.0 in .ruby-version picks the newest installed 4.0.x
- rbenv shell writes a file, just like rbenv local
- rbenv global overrides every project's .ruby-version