Here is my script
#!/usr/bin/env ruby
if __FILE__ == $0
`cd ..`
puts `ls`
end
which runs fine, but when it exits, I'm back where I started. How can I "export" the changes I've made to the environment?
Here is my script
#!/usr/bin/env ruby
if __FILE__ == $0
`cd ..`
puts `ls`
end
which runs fine, but when it exits, I'm back where I started. How can I "export" the changes I've made to the environment?
That's because `
operator is not for complicated scripting. It's useful for running single command (and reading its output). There is no shell behind it to store environment changes between its calls and after your Ruby script is terminated.
On Linux systems each process has its own "current directory" path (it could be found in /proc/‹pid›/cwd). Changing directory in a process does not affect parent processes (shell you run program from). If cd
built-in were binary it could change only its own current directory, not one of the parent process (that's why cd
command could be built-in only).
If Ruby script must be executed from a shell and must affect the shell's current directory path, it's possible to make a "trick". Instead of running commands from Ruby itself, print that commands to stdout and then source
it to the shell you were running Ruby script from. Commands will not be executed by a separate process of a shell, so all cd
s will take effect in the current shell instance.
So, instead of
ruby run_commands.rb
write in your shell-script something like that:
source <(ruby prepare_and_print_commands.rb)
Shell
classBut there is a convenient tool for command line scripting in Ruby — Shell
class! It has predefined shortenings for frequently used commands (such as cd
, pwd
, cat
, echo
, etc.) and allows to define your own (it supports commands and aliases)! Also it transparently supports redirecting of input/output using |
, >
, <
, >>
, <<
Ruby operators.
Working with Shell
is self-explanatory most of the time. Take a look at several simple examples.
Creating a "shell" object and changing directory
sh = Shell.new
sh.cd '~/tmp'
# or
sh = Shell.cd('~/tmp')
Working in the current directory
puts "Working directory: #{sh.pwd}"
(sh.echo 'test') | (sh.tee 'test') > STDOUT
# Redirecting possible to "left" as well as to "right".
(sh.cat < 'test') > 'triple_test'
# '>>' and '<<' are also supported.
sh.cat('test', 'test') >> 'triple_test'
(Note that parentheses are necessary sometimes because of the precedence of "redirecting" operators. Also command output would not be printed to stdout by default, so you need to specify that use > STDOUT
, or > STDERR
if needed.)
Testing file properties
puts sh.test('e', 'test')
# or
puts sh[:e, 'test']
puts sh[:exists?, 'test']
puts sh[:exists?, 'nonexistent']
(Works similar to test
function in a usual shell.)
Defining custom commands and aliases
# name command line to execute
Shell.def_system_command 'list', 'ls -1'
# name cmd command's arguments
Shell.alias_command "lsC", "ls", "-CBF"
Shell.alias_command("lsC", "ls") { |*opts| ["-CBF", *opts] }
Name of the defined command later could be used to run it exactly the same way as it works for predefined echo
or cat
, for example.
Using directory stack
sh.pushd '/tmp'
sh.list > STDOUT
(sh.echo sh.pwd) > STDOUT
sh.popd
sh.list > STDOUT
(sh.echo sh.pwd) > STDOUT
(Here custom list
command defined above is used.)
By the way, there is useful chdir
command to run several commands in a directory and return to previous working directory after that.
puts sh.pwd
sh.chdir('/tmp') do
puts sh.pwd
end
puts sh.pwd
Skip shell object for a group of commands
# Code above, rewritten to drop out 'sh.' in front of each command.
sh.transact do
pushd '/tmp'
list > STDOUT
(echo pwd) > STDOUT
popd
list > STDOUT
(echo pwd) > STDOUT
end
Additional features
In addition Shell
has:
foreach
method to iterate through lines in a file, or through list of files in a directory (depending on whether given path points to a file or a directory),jobs
and kill
commands to control processes,basename
, chmod
, chown
, delete
, dirname
, rename
, stat
, symlink
),File
methods' synonyms (directory?
, executable?
, exists?
, readable?
, etc.),FileTest
class methods' (syscopy
, copy
, move
, compare
, safe_unlink
, makedirs
, install
).