# Author:: Nicolas Pouillard . # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved. # License:: GNU General Public License (GPL). # Revision:: $Id: command.rb 221 2005-05-09 12:40:57Z ertai $ module Commands class Command attr_accessor :command attr_accessor :dir attr_accessor :input attr_accessor :output attr_accessor :error attr_accessor :arg_list # # Construction methods. # def initialize ( command_name, *args ) @command = command_name @arg_list = args.dup end # # Command['wc -l'].sh # def self.[] ( *args ) new(*args) end # # Running methods. # # Simulate Kernel#system() to run the command (No shell expansion). def system unless defined? @@fork_runner @@fork_runner = Runners::Fork.new end run(@@fork_runner) end # Use the ExecRunner. def exec unless defined? @@exec_runner @@exec_runner = Runners::Exec.new end run(@@exec_runner) end # Use Kernel#system() but with a string to have the shell expansion. def sh Kernel.system(to_sh) end # Use Kernel#exec() but with a string to have the shell expansion. def sh! Kernel.exec(to_sh) end # Use Kernel#` and return the resulting string. def expand `#{to_sh}` end # FIXME design me! def ruby end # Use a Commands::Runners::Runner and return a Commands::Datas::Data. def run ( runner=@runner ) runner.run(self) end # # Operators. # # Pipe two commands. def | ( rhs ) Pipe.new(self, rhs) end # Chain two commands (like a `;' in shell), and return a new one. def + ( rhs ) Seq.new(self, rhs) end # Return a new command with the given command input. def < ( arg ) cmd = dup cmd.input = arg cmd end # Return a new command with the given command output. def > ( arg ) cmd = dup cmd.output = arg.dup cmd.output.open_mode = 'w' if cmd.output.is_a? Pathname cmd end # Like > but open the output in append mode. def >> ( arg ) cmd = dup cmd.output = arg.dup cmd.output.open_mode = 'a' if cmd.output.is_a? Pathname cmd end # Add an argument to a command. # cmd = Command.new('ls') # cmd << '-la' # cmd.sh def << ( arg ) @arg_list << arg end # # Misc # def arg_list @arg_list.dup end def arg_string @arg_list.map { |arg| arg.dump }.join(' ').freeze end # # Conversion methods # def to_s str = @command.dump arg_str = arg_string str += ' ' + arg_str unless arg_str.empty? str end def to_sh "#{to_s}#{sh_args}" end def sh_args args = '' args += " < #{@input.to_s.dump}" if @input args += " > #{@output.to_s.dump}" if @output args += " 2> #{@error.to_s.dump}" if @error args end def to_a [@command, *@arg_list].freeze end end # class Command if $0 == __FILE__ require 'test/unit' class CommandTest < Test::Unit::TestCase def setup @file = TempPath.new @file.open('w') { |f| f.puts "bla bla\n foo bar"} ENV['TEST_FILE'] = @file.to_s @touch = Command.new('touch') @cp = Command.new('cp') @cat = Command.new('cat') @wc = Command.new('wc') @dne = Command.new('ThisCommandDoesNotExist') @dev_null = Pathname.new('/dev/null') end def test_0_initialize end def test_self_hook # test_self.[] @cat << 'foo' << 'bar' assert_equal(@cat.to_a, Command[*%w[cat foo bar]].to_a) end def test_system TempPath.new do |tmp| @touch << tmp.to_s @touch.system assert_equal(0, $?) assert(tmp.exist?) end end def test_sh_and_pipe TempPath.new do |tmp| ((@cat | @wc) < @file > tmp).sh assert_equal(0, $?) assert(tmp.exist?) assert_match(/\s*2\s*4\s*17\n/, tmp.read) end end def test_system_and_pipe TempPath.new do |tmp| ((@cat | @wc) < @file.open('r+') > tmp).system assert_equal(0, $?) assert(tmp.exist?) assert_match(/\s*2\s*4\s*17\n/, tmp.read) end end def test_system_is_not_sh TempPath.new do |tmp| @cat << '$TEST_FILE' @cat.error = @dev_null (@cat > tmp).system assert_not_equal(0, $?) assert(!tmp.exist? || tmp.zero?) end end def test_exec TempPath.new do |tmp| @cp << @file << tmp pid = fork do @cp.exec end Process.waitpid pid assert_equal(0, $?) assert(tmp.exist?) assert_equal(@file.read, tmp.read) end end def test_exec_not_sh! TempPath.new do |tmp| @cat << '$TEST_FILE' @cat.error = @dev_null pid = fork do (@cat > tmp).exec end Process.waitpid pid assert_not_equal(0, $?) assert(!tmp.exist? || tmp.size.zero?) end end def test_sh TempPath.new do |tmp| @cat << '$TEST_FILE' (@cat > tmp).sh assert_equal(0, $?) assert(tmp.exist?) assert_equal(@file.read, tmp.read) end TempPath.new do |tmp| ((@cat | @wc) < @file > tmp).sh assert_equal(0, $?) assert(tmp.exist?) assert_match(/\s*2\s*4\s*17\n/, tmp.read) end end def test_sh! TempPath.new do |tmp| @cat << '$TEST_FILE' pid = fork do (@cat > tmp).sh! end Process.waitpid pid assert_equal(0, $?) assert(tmp.exist?) assert_equal(@file.read, tmp.read) end end def test_expand @cat << '$TEST_FILE' str = (@cat).expand assert_equal(0, $?) assert_equal(@file.read, str) str = ((@cat | @wc) < @file).expand assert_equal(0, $?) assert_match(/\s*2\s*4\s*17\n/, str) end def test_ruby # FIXME end def test_pipe # test_| assert_kind_of(Pipe, (@cat | @wc) < @file) end def test_chain # test_+ assert_kind_of(Seq, (@cat + @wc) < @file) TempPath.new do |tmp| TempPath.new do |tmp2| ((@cat < @file > tmp) + (@wc < tmp > tmp2)).sh assert_equal(0, $?) assert(tmp.exist?) assert(tmp2.exist?) assert_equal(@file.read, tmp.read) assert_match(/\s*2\s*4\s*17\n/, tmp2.read) end end end def test_add_input # test_< @cat.input = 'foo' assert_equal('foo', @cat.input) cmd = @cat < 'bar' assert_equal('bar', cmd.input) assert_not_equal(cmd.object_id, @bar.object_id) end def test_add_output # test_> @cat.output = 'foo' assert_equal('foo', @cat.output) cmd = @cat > 'bar' assert_equal('bar', cmd.output) assert_not_equal(cmd.object_id, @bar.object_id) end def test_append_output # test_>> @cat.output = 'foo' assert_equal('foo', @cat.output) cmd = @cat >> 'bar' assert_equal('bar', cmd.output) assert_not_equal(cmd.object_id, @bar.object_id) end def test_arg_list assert_equal([], @cp.arg_list) @cat << 'foo bar' << 'baz' assert_equal(['foo bar', 'baz'], @cat.arg_list) end def test_arg_string assert_equal('', @cp.arg_string) @cat << 'foo bar' << 'baz' assert_equal('"foo bar" "baz"', @cat.arg_string) end def test_to_s assert_equal('"cp"', @cp.to_s) @cat << 'foo bar' << 'baz' assert_equal('"cat" "foo bar" "baz"', @cat.to_s) end def test_to_sh assert_equal('"cp" > "ya"', (@cp > 'ya').to_sh) @cat << 'foo > bar' << '< baz' assert_equal( '(("cat" "foo > bar" "< baz" < "yay") | ("cp" > "yoy"))', ((@cat | @cp) < 'yay' > 'yoy').to_sh) @wc.error = 'foo' assert_equal('"wc" 2> "foo"', @wc.to_sh) end def test_complex TempPath.new do |tmp| ls = 'ls'.to_cmd wc = 'wc'.to_cmd (ls | wc) < @file > tmp end end end # class CommandTest end end # module Commands