syscmd 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Till Salzer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,27 @@
1
+ = syscmd
2
+
3
+ A simple wrapper to execute system commands, which provides stdout, stderr and
4
+ exit code.
5
+
6
+ == Usage
7
+
8
+ require 'syscmd'
9
+ cmd = Syscmd.exec!('command') # => Syscmd::Command object
10
+
11
+ cmd.stdout # => String
12
+ cmd.stderr # => String
13
+ cmd.exitcode # => Integer
14
+
15
+ Note that <code>Syscmd.exec!</code> immediately executes the given command.
16
+ You will get the Syscmd::Command object executed and can examine it.
17
+
18
+
19
+ == Heritage/Acknoledgement
20
+
21
+ The <code>Syscmd::popen</code> code is mostly taken from the original Ruby 1.8
22
+ implementation of Open3::open3 (author: Yukihiro Matsumoto: documentation: Konrad Meyer).
23
+
24
+
25
+ == Copyright
26
+
27
+ Copyright (c) 2009 Till Salzer. See LICENSE for details.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 3
data/lib/syscmd.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'syscmd/popen'
2
+
3
+ # Module.
4
+ module Syscmd
5
+ # Error raise if a command object is executed twice.
6
+ class AlreadyExecutedError < RuntimeError ; end
7
+
8
+ # A command to execute.
9
+ # You usually just use Syscmd::exec! to create and execute the command.
10
+ # A command object can be executed only once.
11
+ class Command
12
+ # +true+ if the command was already executed, otherwise +false+.
13
+ attr_reader :executed
14
+
15
+ # the command to execute.
16
+ attr_reader :cmd
17
+ # array of arguments.
18
+ attr_reader :args
19
+ # the constructed command line.
20
+ attr_reader :cmdline
21
+
22
+ # standard output of the executed command.
23
+ attr_reader :stdout
24
+ # standard error of the executed command.
25
+ attr_reader :stderr
26
+ # exit code of the executed command.
27
+ # Note that the exit code is just a Byte value ranged 0..255 on
28
+ # Unix platforms, so don't expect any negative values or values greated
29
+ # than 255 here.
30
+ attr_reader :exitcode
31
+ # exit status of the executed command.
32
+ attr_reader :process_status
33
+
34
+ # execute a system command.
35
+ # cmd: the command to execute
36
+ # args: command line arguments
37
+ def initialize(cmd, *args)
38
+ @cmd = cmd
39
+ @args = *args
40
+ @process_status = nil
41
+ @executed = false
42
+ end
43
+
44
+ # execute the command.
45
+ # raises AlreadyExecutedError if the command is already executed.
46
+ def exec!
47
+ raise AlreadyExecutedError.new("already executed with status #{process_status}") if self.executed?
48
+ @process_status, pread, perr = Syscmd::popen(self.cmdline)
49
+ @stdout = pread.read
50
+ @stderr = perr.read
51
+ self
52
+ end
53
+
54
+ # get the stdout as lines.
55
+ def stdout_lines
56
+ if @stdout_lines.nil?
57
+ stdout = self.stdout
58
+ return nil unless stdout
59
+ @stdout_lines = stdout.split(/\n/)
60
+ end
61
+ @stdout_lines
62
+ end
63
+
64
+ # get the stdout as lines.
65
+ def stderr_lines
66
+ if @stderr_lines.nil?
67
+ stderr = self.stderr
68
+ return nil unless stderr
69
+ @stderr_lines = stderr.split(/\n/)
70
+ end
71
+ @stderr_lines
72
+ end
73
+
74
+ # the exitcode of the executed command.
75
+ # returns nil
76
+ def exitcode
77
+ @process_status ? @process_status.exitstatus : nil
78
+ end
79
+
80
+ # build the command line for this command.
81
+ # Once the command line is created, it will be cached.
82
+ def cmdline
83
+ if @cmdline.nil?
84
+ cmdline = "#{@cmd}"
85
+ @args.each do |arg|
86
+ cmdline << (arg.to_s.index(" ") ? " \"#{arg}\"" : " #{arg}")
87
+ end if @args
88
+ @cmdline = cmdline
89
+ end
90
+ @cmdline
91
+ end
92
+
93
+ # check if the command was executed.
94
+ def executed
95
+ @process_status.nil? ? false : true
96
+ end
97
+ alias executed? executed
98
+ end
99
+
100
+ # execute a system command.
101
+ # cmd:: the command to execute
102
+ # args:: command line arguments
103
+ # returns:: executed Syscmd::Command object
104
+ def exec!(cmd, *args)
105
+ cmd = Command.new(cmd, args)
106
+ cmd.exec!
107
+ end
108
+ module_function :'exec!'
109
+ end
@@ -0,0 +1,58 @@
1
+ module Syscmd
2
+ # Taken from the original Ruby 1.8 implementation of Open3::open3 and slightly
3
+ # tweaked to return an exit status.
4
+ # This function is meant as in internal method of the Syscmd module; do not use
5
+ # it on it's own, as the interface may change anytime.
6
+ #
7
+ # cmd:: the command to execute
8
+ # returns:: status, stdout, stderr
9
+ #
10
+ # orignal author:: Yukihiro Matsumoto
11
+ # orignal socumentation:: Konrad Meyer
12
+ def popen(*cmd)
13
+ pw = IO::pipe # pipe[0] for read, pipe[1] for write
14
+ pr = IO::pipe
15
+ pe = IO::pipe
16
+ status = 0 # status of the inner fork
17
+
18
+ pid = fork do
19
+ # child
20
+ gcpid = fork do
21
+ # grandchild
22
+ pw[1].close
23
+ STDIN.reopen(pw[0])
24
+ pw[0].close
25
+
26
+ pr[0].close
27
+ STDOUT.reopen(pr[1])
28
+ pr[1].close
29
+
30
+ pe[0].close
31
+ STDERR.reopen(pe[1])
32
+ pe[1].close
33
+
34
+ exec(*cmd)
35
+ end
36
+ gcpid, gcstatus = Process.wait2(gcpid)
37
+ exit!(gcstatus.exitstatus)
38
+ end
39
+
40
+ pw[0].close
41
+ pr[1].close
42
+ pe[1].close
43
+ pid, status = Process.wait2(pid)
44
+
45
+ #pi = [status.exitstatus, pw[1], pr[0], pe[0]]
46
+ pi = [status, pr[0], pe[0]]
47
+ pw[1].sync = true
48
+ if defined? yield
49
+ begin
50
+ return yield(*pi)
51
+ ensure
52
+ pi.each{|p| p.close unless p.closed?}
53
+ end
54
+ end
55
+ pi
56
+ end
57
+ module_function :popen
58
+ end
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Syscmd::Command, ".exec! for -s 'Hello World'" do
4
+ subject { Syscmd::Command.new(TESTER, '-s', 'Hello World').exec! }
5
+
6
+ it "should have stdout 'Hello World\\n'" do
7
+ subject.stdout.should == "Hello World\n"
8
+ end
9
+ it "should have stdout_lines ['Hello World']" do
10
+ subject.stdout_lines.should == ["Hello World"]
11
+ end
12
+
13
+
14
+ it "should have stderr \"\"" do
15
+ subject.stderr.should == ""
16
+ end
17
+
18
+ it "should have exitcode 0" do
19
+ subject.exitcode.should == 0
20
+ end
21
+ end
22
+
23
+ describe Syscmd::Command, ".exec! for multiline output" do
24
+ subject { Syscmd::Command.new(TESTER,
25
+ '-s', 'Hello World', '-S', 2,
26
+ '-e', 'Bye Bye World', '-E', 2).exec! }
27
+
28
+ it "should have stdout 'Hello World\\nHello World\\n'" do
29
+ subject.stdout.should == "Hello World\nHello World\n"
30
+ end
31
+ it "should have two strings in stdout_lines" do
32
+ subject.stdout_lines.should == ["Hello World", "Hello World"]
33
+ end
34
+
35
+ it "should have stderr 'Bye Bye World\\nBye Bye World\\n'" do
36
+ subject.stderr.should == "Bye Bye World\nBye Bye World\n"
37
+ end
38
+ it "should have two strings in stdout_lines" do
39
+ subject.stderr_lines.should == ["Bye Bye World", "Bye Bye World"]
40
+ end
41
+ end
42
+
43
+ describe Syscmd::Command, ".exec! for positive exit codes" do
44
+ [1, 2, 254, 255].each do |st|
45
+ it "should return exit code #{st} for -x #{st}" do
46
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
47
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
48
+ cmd.exec!
49
+ cmd.exitcode.should == st
50
+ end
51
+ end
52
+ end
53
+
54
+ describe Syscmd::Command, ".exec! for hugh positive exit codes" do
55
+ [256, 257, 512, 4000].each do |st|
56
+ it "should return exit code #{st % 256} for -x #{st}" do
57
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
58
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
59
+ cmd.exec!
60
+ cmd.exitcode.should == st % 256
61
+ end
62
+ end
63
+ end
64
+
65
+ describe Syscmd::Command, ".exec! for negative exit codes" do
66
+ [-1, -2, -255, -256].each do |st|
67
+ it "should return exit code #{st % 256} for -x #{st}" do
68
+ cmd = Syscmd::Command.new(TESTER, '-x', st)
69
+ cmd.cmdline.should == "#{TESTER} -x #{st}"
70
+ cmd.exec!
71
+ cmd.exitcode.should == st % 256
72
+ end
73
+ end
74
+ end
75
+
76
+ describe Syscmd::Command, ".exec! called twice" do
77
+ subject { Syscmd::Command.new(TESTER) }
78
+ it "should fail with AlreadyExecutedError" do
79
+ subject.exec!
80
+ lambda { subject.exec! }.should raise_error(Syscmd::AlreadyExecutedError)
81
+ end
82
+ end
@@ -0,0 +1,29 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Syscmd::Command, ".new" do
4
+ subject { Syscmd::Command.new(TESTER) }
5
+
6
+ it "should have #{TESTER} as cmd attribute" do
7
+ subject.cmd.should == TESTER
8
+ end
9
+
10
+ it "should not be executed after creation" do
11
+ subject.executed?.should == false
12
+ end
13
+ end
14
+
15
+ describe Syscmd::Command, ".new with -s 'Hello World'" do
16
+ subject { Syscmd::Command.new(TESTER, '-s', 'Hello World') }
17
+
18
+ it "should have the cmd attribute #{TESTER}" do
19
+ subject.cmd.should == TESTER
20
+ end
21
+
22
+ it "should have the command line arguments ['-s', 'Hello World']" do
23
+ subject.args.should == ['-s', 'Hello World']
24
+ end
25
+
26
+ it "should have the command line '#{TESTER} -s \"Hello World\"'" do
27
+ subject.cmdline.should == "#{TESTER} -s \"Hello World\""
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'syscmd'
6
+
7
+ TESTER=File.join(File.dirname(__FILE__), 'tester.rb') unless defined?(TESTER)
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+ require 'spec_helper'
3
+
4
+ describe Syscmd, ".execute" do
5
+ subject { Syscmd }
6
+
7
+ it "should provide a .execute method" do
8
+ subject.should respond_to(:exec!)
9
+ end
10
+
11
+ it "should return a Syscmd::Command object" do
12
+ subject.exec!(TESTER).should be_a(Syscmd::Command)
13
+ end
14
+
15
+ it "should return an executed Syscmd::Command object" do
16
+ cmd = subject.exec!(TESTER)
17
+ cmd.executed?.should == true
18
+ end
19
+
20
+ end
data/spec/tester.rb ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # == Synopsis
3
+ #
4
+ # tester: test script for syscmd
5
+ #
6
+ # == Usage
7
+ #
8
+ # tester [OPTION]
9
+ #
10
+ # OPTIONS:
11
+ # --help, -h display this page
12
+ # --stdout, -s MSG echo MSG to stdout
13
+ # --stdoutrepeat, -S COUNT print the stdout message COUNT times
14
+ # (DEFAULT: 1)
15
+ # --stderr, -e MSG echo MSG to stderr
16
+ # --stderrrepeat, -E COUNT print the stderr message COUNT times
17
+ # (DEFAULT: 1)
18
+ # --exitcode, -x CODE return exit code CODE
19
+ # --debug
20
+
21
+ require "getoptlong"
22
+ require "rdoc/usage"
23
+
24
+ opts = GetoptLong.new(
25
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
26
+ [ '--stdout', '-s', GetoptLong::REQUIRED_ARGUMENT ],
27
+ [ '--stdoutcount', '-S', GetoptLong::REQUIRED_ARGUMENT ],
28
+ [ '--stderr', '-e', GetoptLong::REQUIRED_ARGUMENT ],
29
+ [ '--stderrcount', '-E', GetoptLong::REQUIRED_ARGUMENT ],
30
+ [ '--exitcode', '-x', GetoptLong::REQUIRED_ARGUMENT ],
31
+ [ '--debug', GetoptLong::OPTIONAL_ARGUMENT ]
32
+ )
33
+
34
+ options = {}
35
+ exitcode = 0
36
+
37
+ msg_stdout = nil
38
+ count_stdout = 1
39
+
40
+ msg_stderr = nil
41
+ count_stderr = 1
42
+
43
+ opts.each do |opt, arg|
44
+ case opt
45
+ when '--help'
46
+ RDoc::usage
47
+ when '--stdout'
48
+ msg_stdout = arg
49
+ when '--stdoutcount'
50
+ count_stdout = arg.to_i
51
+ when '--stderr'
52
+ msg_stderr = arg
53
+ when '--stderrcount'
54
+ count_stderr = arg.to_i
55
+ when '--exitcode'
56
+ exitcode = arg.to_i
57
+ when '--debug'
58
+ # not yet implemented
59
+ end
60
+ end
61
+
62
+ if msg_stdout
63
+ count_stdout.times do
64
+ $stdout.print "#{msg_stdout}\n"
65
+ end
66
+ end
67
+
68
+ if msg_stderr
69
+ count_stderr.times do
70
+ $stderr.print "#{msg_stderr}\n"
71
+ end
72
+ end
73
+
74
+ exit exitcode
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: syscmd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Till Salzer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-18 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: till.salzer@googlemail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE
25
+ files:
26
+ - README.rdoc
27
+ - VERSION.yml
28
+ - lib/syscmd/popen.rb
29
+ - lib/syscmd.rb
30
+ - spec/command_exec_spec.rb
31
+ - spec/command_spec.rb
32
+ - spec/spec_helper.rb
33
+ - spec/syscmd_spec.rb
34
+ - spec/tester.rb
35
+ - LICENSE
36
+ has_rdoc: true
37
+ homepage: http://github.com/tsalzer/syscmd
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --inline-source
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.4
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: Wrapper for OS command execution
65
+ test_files: []
66
+