Forum: Ruby Ruby for system administration

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Tassilo Horn (Guest)
on 2006-04-04 14:20
(Received via mailing list)
Hi,

since I don't like bash or other shell languages too much, I've started
writing more and more system administration scripts in ruby, which works
great and saves much time. I invoke the commands which should be
executed with 'Kernel.`'.

But there's one problem with that. If my_command produces output when
being executed directly from the shell prompt, I won't see it when I
call it in a ruby script with `my_command`.

To get along this I normally use

    $stdout << `my_command`

but then the output will not be shown until my_command finishes. If
my_command takes some time, the user running the script will have no
feedback.

Is there a way to directly redirect my_command's output to stdout? Or
are there other ways to work arround this?

Much thanks in advance,
Tassilo
Farrel L. (Guest)
on 2006-04-04 14:36
(Received via mailing list)
You might want to look at IO.popen. It allows you to run a command and
communicate with it (and receive output) via pipes which to your
script will just be an IO object with all the associated methods like
readlines,puts etc etc. I'm using it in a project currently and it's
quite easy to use.

Farrel
Damphyr (Guest)
on 2006-04-04 15:29
(Received via mailing list)
Tassilo Horn wrote:

> Is there a way to directly redirect my_command's output to stdout? Or
>  are there other ways to work arround this?
I use IO.popen and Observer to get progress reports for commands.
I have been building an automation framework together with what I call a
test integration framework for my current project.
At th emoment the project is in full steam and there is absolutely no
chance to release the code for public consumption (meaning there is no
time to remove project specific stuff and wrap the whole thing as a
public library that is half-way stable).
Just so, here's what I have done in the lowest level. A Command module
to define a stable interface and DOSCommand which I use to execute
commands on a windows shell (which in turn uses the class that led to
this whole Command thing and which actually is my answer to your
question :) ).
I have several commands, my favorite being the PressAnyKeyCommand :)


#This module is the interface for execution of arbitrary commands where
we
     #need access to the success status, output and execution time for a
command
     #
     #Check DOSCommand and RubyCommand for some implementations
     module Command
       attr_writer :output
       def name
         return @name
       end
       def output
         init
         return @output
       end
       def working_directory
         init
         return @working_directory
       end
       def exec_time
         init
         return @exec_time
       end
       def success?
         init
         return @success
       end
       def run?
         init
         return @run
       end

       def run
         init
         @run=true
       end

       def status verbose=false
         init
         msg=""
         if run?
           st="succeeded"
           st="failed" unless @success
           msg+="#{@name} #{st} in #{@exec_time}s\n"
         else
           msg+="#{@name} was not executed\n"
         end
         msg+="Working directory:
#{@working_directory}\nLog:\n#{@output}\n" if (!success?||verbose)
         return msg
       end
       #This method initializes the module members with sane values.
       #
       #It also checks that _working_directory_ is a directory (it
raises RuntimeError if it isn't)
       #
       #It's a good idea to use this method in the initialize method of
any objects including this module
       #but it isn't necessary.
       def init name="",working_directory=nil
         begin
           @name=name
           @output="" unless @output
           @exec_time=0 unless @exec_time
           @working_directory=working_directory
           @working_directory=Dir.pwd unless @working_directory
           raise "Non-existent working directory: #{working_directory}"
unless File.directory?(@working_directory)
           @run=false unless @run
           @success=false unless @success
         end unless @name
       end
     end
     #Class that wraps the execution of a command in a command shell.
     class DOSCommand
       include Command
       attr_reader:command_line
       #If _working_directory_ is specified then the command will be
executed in that directory.
       def initialize name,command_line,working_directory=nil
         init(name,working_directory)
         @command_line=command_line
         @cmd=RivaLib::Win::ExecCmd.new(@command_line)
       end

       #Runs the command.
       def run
         @run=true
         prev_dir=Dir.pwd
         begin
           Dir.chdir(@working_directory) if @working_directory
           @cmd.run
           @success=@cmd.success?
           @output=@cmd.output
           @exec_time=@cmd.exec_time
           return @success
         rescue SystemCallError
           return false
         ensure
           Dir.chdir(prev_dir)
         end
       end

#Executes a command on a dos shell, redirecting stderr to stdout
("2>&1")
     #
     #You can then access the output and the return value for the
command.
     #
     #This is meant as a last-resort replacement for popen3 (because of
problems with VC++6.0 and the Ruby O.-Click Installer).
     #
     #_exec_time_ provides the Time spent running the command.
     class ExecCmd
       attr_reader :output,:cmd,:exec_time
       #When a block is given, the command runs before yielding
       def initialize cmd
         @output=""
         @exec_time=0
         @cmd=cmd
         @cmd_run=cmd+" 2>&1" unless cmd=~/2>&1/
         if block_given?
           run
           yield self
         end
       end

       #Runs the command
       def run
         t1=Time.now
         IO.popen(@cmd_run){|f|
           @output=f.read
           @process=Process.waitpid2(f.pid)[1]
         }
         @exec_time=Time.now-t1
       end
       #Returns false if the command hasn't been executed yet
       def run?
         return false unless @process
         return true
       end
       #Returns the exit code for the command.
       #
       #Returns nil if the command hasn't run yet.
       def exitcode
         return @process.exitstatus if @process
         return nil
       end
       #Returns true if the command was succesfull.
       #
       #Will return false if the command hasn't been executed
       def success?
         return @process.success? if @process
         return false
       end
     end


--
http://www.braveworld.net/riva
Tassilo Horn (Guest)
on 2006-04-04 16:21
(Received via mailing list)
"Farrel L." <removed_email_address@domain.invalid> writes:

Hi Farrel,

> You might want to look at IO.popen. [...]

I'll have a look at it later this day. But the things you mentioned
sound quite good, I think.

Thanks,
Tassilo
Florian F. (Guest)
on 2006-04-05 11:41
(Received via mailing list)
Tassilo Horn wrote:

>Is there a way to directly redirect my_command's output to stdout? Or
>are there other ways to work arround this?
>
>
system 'my_command' does this.
This topic is locked and can not be replied to.