Coder's Cat

Ruby change current working directory

2020-04-02

file:img/CAPTURE-2020_04_02_ruby-change-working-directory.org_20200402_232121.png

There are two ways to change the current working directory in Ruby, but you need to notice the thread-safe issue.

Dir.chdir

Dir.chdir is the most traight way:

Dir.chdir('/tmp')

If you need to go back to the previous directory:

orig_dir = Dir.pwd
Dir.chdir('/tmp')
.....

Dir.chdir(org_dir)

Dir.chdir with block

A more idiomatic way is using a block to finish the task and go back to the previous directory:

Dir.chdir('/tmp') do
.....
end

The original working directory is restored when the block exits. The return value of chdir is the value of the block.

But, this is not thread-safe.

Dir.chdir will call the POSIX function to change the working dir of the current process, so it can never be made safe across threads.

MRI adds a warning message for this scenario, let’s run this code snippet:

require 'fileutils'

FileUtils.mkdir_p("/tmp/foo")

def blah(d)
Dir.chdir(d) do
sleep 2
end
end

threads = []
threads << Thread.new { blah('/tmp/foo') }
threads << Thread.new { blah('/tmp/foo') }
threads.each { |t| t.join }

The will be a warning message printed out:

./chdir_demo.rb:3: warning: conflicting chdir during another chdir block

How to be thread-safe

If you want to make this block to be safe-thread, you need to add Mutex for it:

require 'thread'

M = Mutex.new

def blah(d)
M.synchronize do
Dir.chdir(d) do
puts Dir.pwd
sleep 2
end
end
end

threads = []
threads << Thread.new { blah('/tmp/foo') }
threads << Thread.new { blah('/tmp/foo') }
threads.each { |t| t.join }

Join my Email List for more insights, It's Free!😋

Tags: Misc