65

I know how to run a shell command in Ruby like:

%x[#{cmd}]

But, how do I specify a directory to run this command?

Is there a similar way of shelling out, similar to subprocess.Popen in Python:

subprocess.Popen(r'c:\mytool\tool.exe', cwd=r'd:\test\local')

Thanks!

James Taylor
  • 6,158
  • 8
  • 48
  • 74
icn
  • 17,126
  • 39
  • 105
  • 141

6 Answers6

153

You can use the block-version of Dir.chdir. Inside the block you are in the requested directory, after the Block you are still in the previous directory:

Dir.chdir('mydir'){
  %x[#{cmd}]
}
knut
  • 27,320
  • 6
  • 84
  • 112
  • 10
    The things like this that Ruby does with blocks never cease to amaze me. Ruby constantly makes my other languages feel clunky and overcomplicated. – bta Apr 13 '12 at 21:23
  • 7
    Dir.chdir is ok if you have a single thread. For multiple threads look at the other answers here. – neoneye Jul 29 '14 at 17:59
  • This is great for single-threaded scripts. It is not safe for launching multiple processes in parallel. – John Aug 01 '18 at 18:04
12

Ruby 1.9.3 (blocking call):

require 'open3'
Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t|
  p o.read.chomp #=> "/"
}

Dir.pwd #=> "/home/abe"
Abe Voelker
  • 30,124
  • 14
  • 81
  • 98
4

also, taking the shell route

%x[cd #{dir} && #{cmd}]
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
4

The closest I see to backtricks with safe changing dir is capture2:

require 'open3'
output, status = Open3.capture2('pwd', :chdir=>"/tmp")

You can see other useful Open3 methods in ruby docs. One drawback is that jruby support for open3 is rather broken.

akostadinov
  • 17,364
  • 6
  • 77
  • 85
  • 2
    Using the `:chdir` option of any `Process.spawn`-based invokation (`system`, `popen3`,...) seems to be thread-safe for launching parallel processes, too. – John Aug 01 '18 at 18:05
1

Maybe it's not the best solution, but try to use Dir.pwd to get the current directory and save it somewhere. After that use Dir.chdir( destination ), where destination is a directory where you want to run your command from. After running the command use Dir.chdir again, using previously saved directory to restore it.

parallelgeek
  • 438
  • 2
  • 11
  • 3
    You can also use the block-version of `Dir.chdir`. Inside the block you are in the requested directory, after the Block you are still in the previous directory. – knut Apr 13 '12 at 20:48
  • @knut You should make that an answer - I like it! I was going to suggest something crazy like `Dir.chdir(Dir.pwd.tap {Dir.chdir('d:\test\local'); #otherstuff})` as I wasn't aware chdir could take a block – Abe Voelker Apr 13 '12 at 20:53
  • @AbeVoelker You are right, [here it is](http://stackoverflow.com/a/10148325/676874) – knut Apr 13 '12 at 21:08
1

I had this same problem and solved it by putting both commands in back ticks and separating with '&&':

`cd \desired\directory && command`
rmsimpsonau
  • 43
  • 1
  • 8