8

I'd like to know if it's possible to change the current terminal directory from where I'm executing my ruby script.

For example, if I'm executing the script from $HOME in the terminal, I would like to cd in $HOME/mydir at the end of the script.

I've tried a couple of things, but I'm always finding in the same directorry where the script was started.

Things I've tried:

Dir.chdir( mydir )
%[cd mydir]

They actually do change directory, but only in the thread of the script execution. When the script ends, my current location in the terminal is still the same I was before launching the script.

I've found something similar in SO, but it's Python-related and response seems negative.


You may ask why?

I'm currently involved in a command line application (using gli) which, as starting point, needs a project folder. So my first command looks like:

$ myfoo new project

Which creates a new folder with name project in the current directory. Now to be able to work with the other commands I need to:

$ cd project
project$ myfoo domore

So I was simply thinking that it would be nice to found my self in project after having executed myfoo new project.


As I was afraid, it's not that easy (not possible at all). So what about using a bash script which does the ruby calls that I want, in place of the standard file generated by RubyGems? Is that feasible? Am I foolish? Should I use eval?


Thanks for the answers. But I'm afraid that no one is acceptable. Probably the question wasn't :).

Community
  • 1
  • 1
Emiliano Poggi
  • 24,390
  • 8
  • 55
  • 67
  • It seems unlikely you would be able to do this. Any system calls from ruby operate in a different thread from your script/shell, and would have no effect on the current location. What are you hoping to use this for? – Douglas F Shearer May 25 '11 at 22:11
  • @Douglas-F-Shearer: I've extended my reasons a bit in the answer – Emiliano Poggi May 25 '11 at 22:18
  • just out of curiosity, why didn't my answer work? I've used this technique a couple times, and I'm unclear how it doesn't suit your situation. – Ian May 26 '11 at 21:43
  • @Ian: your answer works, but is not easy applicable to my situation. I'm having five commands (maybe more in the future) and I want to stay with a `rubygem`. I should integrate the thing at the level of the command execution (see the `new` command [here](https://github.com/3mime/RuGPost/blob/master/lib/rugpost/commands/project_commands.rb)) – Emiliano Poggi May 27 '11 at 21:08

4 Answers4

2

This isn't possible, to my knowledge, since they're different processes. If you need to affect the Ruby script's parent process (Bash), Bash will need to source something. So you could have your Ruby script output some bash commands, then have a Bash wrapper that source's Ruby's output.

Try this as an example, in Bash:

`ruby -e 'puts "cd /tmp"'` | source /dev/stdin

But this will only work in Bash 4.0.

EDIT:

Actually, you can do this with eval instead of source in Bash 3:

eval $(ruby -e'puts "cd /tmp"')
Community
  • 1
  • 1
Ian
  • 2,078
  • 1
  • 17
  • 27
  • Ok, but I end in my script with `%[eval $(ruby -e'puts "cd /tmp"')]`, which does not make any difference. – Emiliano Poggi May 25 '11 at 22:37
  • right, because there you're spawning a child Bash process (a grandchild of the Bash process you want to affect), evaling your code in it, and then letting it die. You need to run eval directly in the Bash process you want to change. Later on, you can tidy it up with an alias or something. – Ian May 25 '11 at 22:43
  • I've extended the answer which asks the thing the other way around. – Emiliano Poggi May 25 '11 at 22:56
  • you're *still* going to need to call source from the current bash process. You can't get around that. So, you can have a bash script that looks like `eval $(ruby my_script_that_outputs_bash.rb)`, and you'll call it like `source my_script_that_evals.sh`. – Ian May 25 '11 at 23:17
2

I doubt there is a straightforward way to do this.

When you launch a program from your login shell, underneath the hood it's doing a fork/exec which means a new (child) process is created (which has its own environment, such as current working directory) and it's standard input/output/error streams are associated with the (parent) shell process. So when your script calls Dir.chdir it changes the current working directory for the script (child) process, not the parent.

Perhaps your script could launch a new shell before it exits, so it looks like your terminal has changed directory. Although, you'd have to exit from that extra shell before exiting your terminal session...

maerics
  • 151,642
  • 46
  • 269
  • 291
1

Maybe slightly easier,

You can use exec in combination with Maerics post about creating a new bash shell inside of ruby to achieve this.

exec ruby -e "Dir.chdir( 'mydir' ); exec 'bash'"

exec will replace the parent process(shell) with the child process (ruby), thus the shell no longer exists (no need to exit).

At the end of the ruby routine bash is fired up (again with exec this time in ruby) in the new directory and replaces the ruby process.

diedthreetimes
  • 4,086
  • 26
  • 38
  • How do I integrate this with a rubygem which has its own command, like the one described in the answer? – Emiliano Poggi May 25 '11 at 23:13
  • @empo At the end the ruby script add `exec 'bash'`. From the command line, modify the call to be `exec myfoo new project`. This is still clunky though :( – diedthreetimes May 25 '11 at 23:29
0

Midnight Commander does this, by using a shell alias and a wrapper-script.

The wrapper is for cd'ing to a directory determined by the contents of a file written by mc(1) at exit, and the alias is used to source the wrapper, which makes it execute in the login shell, instead of the subshell that would have been forked, had the script been launched normally.

All in all, this lets it change the current working directory on exit.

To do this, you'd need to duplicate the alias/wrapper functionality. See http://midnight-commander.org/browser/contrib for how they did it.

kvs
  • 1