Making Environment Changes Stick in Ruby Scripting
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 开发者_如何学Cmade to the environment?
That is because the backtick operator is not intended for complicated scripting. It is useful for running a single command (and capturing 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 (the shell you run program from). If
the cd
built-in were a binary, it could change only its own current
directory, not the one of the parent process, and that is why the cd
command could be built-in only.
An alternative implementation
If a Ruby script must be executed from shell and must affect the
shell’s current directory path, the following trick may be used.
Instead of running commands from within Ruby, print those commands to
the standard output, and then source
it to the shell you are running
the Ruby script from. The 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)
The Shell
class
But there is a convenient tool for command line scripting in Ruby: the
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). It also
transparently supports redirection of the standard input and output
streams using |
, >
, <
, >>
, <<
Ruby operators.
Working with Shell
is self-explanatory most of the time. Take a look
at the following straightforward examples.
Creating a Shell
object and changing the current directory
sh = Shell.new
sh.cd '~/tmp'
# or
sh = Shell.cd('~/tmp')
Working within 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 the redirection operators. Further, the output
of a command is not printed to 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] }
The name of a defined command can be used to run it later (in exactly
the same way as it works with predefined echo
or cat
, for example).
Using the directory stack
sh.pushd '/tmp'
sh.list > STDOUT
(sh.echo sh.pwd) > STDOUT
sh.popd
sh.list > STDOUT
(sh.echo sh.pwd) > STDOUT
Here the custom list
command defined above is used.
By the way, there is a convenient 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 the 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, the Shell
class has:
- the
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); - the
jobs
andkill
commands to control processes; - a bunch of commands for manipulating files, such as
basename
,chmod
,chown
,delete
,dirname
,rename
,stat
, andsymlink
; - a number of
File
methods’ synonyms:directory?
,executable?
,exists?
,readable?
, etc.; - the equivalents to the
FileTest
class methods:syscopy
,copy
,move
,compare
,safe_unlink
,makedirs
, andinstall
.
精彩评论