开发者

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 cds 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 and kill commands to control processes;
  • a bunch of commands for manipulating files, such as basename, chmod, chown, delete, dirname, rename, stat, and symlink;
  • 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, and install.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜