5

I'd like to change the pwd of the current shell from within a ruby script. So:

> pwd
/tmp
> ruby cdscript.rb
> pwd
/usr/bin

This is the code I have right now:

exec('cd /usr/bin')

Unfortunately, cd is a builtin command. So:

`exec': No such file or directory - cd (Errno:ENOENT)

Any workaround for this?


No way to get it working in ruby itself, so I switched gears. I modified the script to output the target directory path and then defined a function in .bashrc that would pass the arguments through the script and then cd to the right directory. Not self-contained as I would have hoped, but it did the job.

Thanks for the replies, folks.

Joe Mastey
  • 26,809
  • 13
  • 80
  • 104
  • You can try `cd /usr/bin` or system('cd /usr/bin') in your ruby code, but this will only change current directory of new shell (which is child process of ruby which is child of the shell), but this is pointless becouse I thing that you can't change the directory of parent process in *inx system. – jcubic Aug 04 '10 at 22:27
  • How about a shell solution? Like putting _alias usb="cd /usr/bin"_ in a bash profile and then typing _usb_ in shell. – Nikita Rybak Aug 04 '10 at 22:28
  • there's a bunch of code logic to get to the exec portion (it's not actually just a static path). this is just a trivial example, so alias is less than optimal. – Joe Mastey Aug 04 '10 at 22:30
  • Isn't a script that can change the current working directory (and hence, presumably a great number of environment variables) to be considered a serious security hazard? This is why .rvmrc makes me nervous, even with its "trust" mechanism. – saurabh Oct 13 '11 at 06:37

7 Answers7

14

Dir.chdir("/usr/bin") will change the pwd within the ruby script, but that won't change the shell's PWD as the script is executed in a child process.

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • 3
    Depends on how you look at it, he was having a problem trying to exec() 'cd', which will (also) never work. If he had asked about why Dir.chdir() wasn't working then this answer would have been totally irrelevant. Anyway, edited for completeness. – Vinko Vrsalovic Aug 04 '10 at 22:28
  • 2
    @sepp2k : It was the answer I was looking for, and it works very well, thanks for Vinko. – Dorian Mar 26 '12 at 16:33
8

You won't be able to change the working directory of your shell.

When it executes ruby, it forks so you get a new environment, with a new working directory.

Bertrand Marron
  • 21,501
  • 8
  • 58
  • 94
6

Stumbled across this while searching to do the same thing.

I was able to solve this by running multiple statements within the backticks.

'cd #{path} && <statement> && cd ..'
Leonard Teo
  • 1,238
  • 1
  • 17
  • 28
4

As other answers already pointed out, you can change the pwd inside your ruby script, but it only affects the child process (your ruby script). A parent process' pwd cannot be changed from a child process.

One workaround could be, to have the script output the shell command(s) to execute and call it in backticks (i.e. the shell executes the script and takes its output as commands to execute)

myscript.rb:

# … fancy code to build somepath …
puts "cd #{somepath}"

to call it:

`ruby myscript.rb`

make it a simple command by using an alias:

alias myscript='`ruby /path/to/myscript.rb`'

Unfortunately, this way you can't have your script output text to the user since any text the script outputs is executed by the shell (though there are more workarounds to this as well).

Zargony
  • 9,615
  • 3
  • 44
  • 44
0

You can't change the working directory (or the environment variables for that matter) of the shell that invoked you in a ruby script (or any other kind of application). The only commands that can change the pwd of the current shell are shell built-ins.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
0

You can try to use system instead of exec. It works for me.

Like system("cd #{new_path} && <statement> && cd #{original_path}")

Winbobob
  • 379
  • 4
  • 7
0

As other answers have pointed out, running either system("cd foo") or `cd foo` within a Ruby script will only change the directory of the Ruby subprocess. As soon as the Ruby script finishes, you'll get popped back to the directory you were in before.

A way around this is to have the Ruby script only be responsible for printing out a directory name and then calling that script with a wrapping shell function that cds into the directory the Ruby script prints out.

If you'd like your Ruby script to interact with the user use STDERR.puts instead of puts.

Here's an example Ruby script cd.rb that will try to find a matching directory from user input:

#!/usr/bin/env ruby

pattern = ARGV.join(" ")
dirs = Dir.glob("*").filter{|path| File.directory?(path)}.filter{|path| path.include?(pattern)}

if dirs.length == 0
  STDERR.puts "Error: no matching directories found."
  print "."
  exit 1
elsif dirs.length == 1
  print dirs.first
else
  while true do
    STDERR.puts "Which one?"
    dirs.each_with_index{|dir, i|
      STDERR.puts "#{i+1}. #{dir}"
    }
    STDERR.print "=> "
    i = STDIN.gets.chomp.to_i - 1
    if dirs[i]
      print dirs[i]
      exit
    else
      STDERR.puts "Error: uhh what? Try that again...\n\n"
    end
  end
end

And here's the wrapping sh or zsh script to stick in your .bashrc or .zshrc that will pass along arguments to cd.rb and then cd into the directory that Ruby script prints out:

function ,cd () {
  cd "`cd.rb $@`"
}
cgenco
  • 3,370
  • 2
  • 31
  • 36