Skip to content


Search $PATH for a binary rather than shelling out to `which` #1573

merged 1 commit into from

6 participants


I'm trying to speed up startup time for rake environment (on rails). Creating subshells came up on my list, so I'm trying to eliminate them.

This patch changes Bundler to search the PATH environment variable rather than shelling out to which. The function isn't 100% perfect as it doesn't handle "./sudo" the same way the shell does, but our input for this function is known so I don't think it matters.

Bundler member

Looks good. Thanks for your continued work on Rails performance improvements.

@sferik sferik merged commit 36c9f92 into bundler:master

@tenderlove - just curious... did you benchmark the difference (i.e. is the newer code actually faster than shelling out)?

Bundler member

@fxn addressed your nitpick in 26fa77a.

fxn commented

@sferik that was fast :).



require 'rubygems'
require 'benchmark/ips'

def sand_which cmd
  `which #{cmd}`

def blair_which cmd
  if File.executable? cmd
    path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |path|
      File.executable? File.join(path, cmd)
    path && File.expand_path(cmd, path)

Benchmark.ips do |x|"sand") { sand_which 'sudo' }"blair") { blair_which 'sudo' }
[aaron@higgins git]$ ruby lol.rb 
                sand      251.0 (±4.8%) i/s -       1265 in   5.051489s (cycle=23)
               blair     3222.0 (±8.2%) i/s -      16218 in   5.076616s (cycle=306)
[aaron@higgins git]$

It's over 10x faster. Probably because the one that shells out has to shell out.


That's a pitty: I proposed the same change on 2011-11-03 (#1516)... Today, nobody even had a look at it :(
Happy to see it merged by another channel, but kind of puzzled with the way mine has been handled ... Definitively not a way to encourage people to participate.

@sleeper sleeper referenced this pull request

which: no sudo in ... #1321


ENV['PATH'] is sometimes nil and breaks in a production environment I have with Passenger... Can we change it to ENV['PATH'].to_s or something to avoid that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Showing with 12 additions and 1 deletion.
  1. +12 −1 lib/bundler.rb
13 lib/bundler.rb
@@ -229,7 +229,7 @@ def requires_sudo?
path = bundle_path
path = path.parent until path.exist?
- sudo_present = !(`which sudo` rescue '').empty?
+ sudo_present = which "sudo"
bin_dir =
bin_dir = bin_dir.parent until bin_dir.exist?
@@ -246,6 +246,17 @@ def mkdir_p(path)
+ def which(binary)
+ if File.executable? binary
+ binary
+ else
+ path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |path|
+ File.executable? File.join(path, binary)
+ }
+ path && File.expand_path(binary, path)
+ end
+ end
def sudo(str)
`sudo -p 'Enter your password to install the bundled RubyGems to your system: ' #{str}`
Something went wrong with that request. Please try again.