syscmd 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+