shell-executer 1.0.0

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.
Files changed (5) hide show
  1. data/README +61 -0
  2. data/Rakefile +68 -0
  3. data/lib/shell/executer.rb +132 -0
  4. data/test/test_executer.rb +50 -0
  5. metadata +80 -0
data/README ADDED
@@ -0,0 +1,61 @@
1
+ = shell-executer
2
+
3
+ Shell::Executer provides an easy and robust way to execute shell commands.
4
+ The stdout and stderr data can be read completely after the execution or
5
+ in chunks during the execution. If the data is processed in chunks this could
6
+ be done by line or by char.
7
+
8
+ Command execution is done utilizing open4[http://rubyforge.org/projects/codeforpeople/].
9
+
10
+ by {Holger Kohnen}[http://www.holgerkohnen.de]
11
+
12
+ == Download and Installation
13
+
14
+ You can download shell-executer from here[http://rubyforge.org/projects/shell-executer/] or install it with the following command.
15
+
16
+ $ gem install shell-executer
17
+
18
+ == License
19
+
20
+ You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt).
21
+
22
+ == Usage
23
+ require 'shell/executer.rb'
24
+
25
+ # execute and read standard output
26
+ Shell.execute('echo hello').stdout # => "hello\n"
27
+
28
+ # execute and read error output
29
+ Shell.execute('echo >&2 error').stderr # => "error\n"
30
+
31
+ # execute and check if the command exited with success
32
+ Shell.execute('ls /tmp').success? # => true
33
+ Shell.execute('ls /not_existing').success? # => false
34
+
35
+ # execute and process every line of output with block
36
+ Shell.execute('echo hello; echo world') {|stdout| print stdout }
37
+ # => "hello\n"
38
+ # => "world\n"
39
+
40
+ # execute and process every char of output with block
41
+ Shell.execute('echo 42', :mode => :chars) {|stdout| puts stdout }
42
+ # => "4\n"
43
+ # => "2\n"
44
+
45
+ # execute and process every line of stdout or stderr
46
+ Shell.execute('echo hello; echo >&2 error') do |stdout, stderr|
47
+ if stdout
48
+ print 'OUT> %s' % stdout
49
+ else
50
+ print 'ERR> %s' % stderr
51
+ end
52
+ end
53
+ # => "OUT> hello\n"
54
+ # => "ERR> error\n"
55
+
56
+ # execute and throw an exception if the process exited without success
57
+ begin
58
+ Shell.execute!('ls /not_existing')
59
+ rescue RuntimeError => e
60
+ print e.message # <= prints stderr
61
+ end
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require 'rubygems'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/contrib/sshpublisher'
6
+
7
+ TITLE = 'shell-executer'
8
+ DESCRIPTION = <<-EOS
9
+ Shell::Executer provides an easy and robust way to execute shell commands.
10
+ The stdout and stderr data can be read completly after the execution or
11
+ in chunks during the execution.
12
+ EOS
13
+ AUTHOR = 'Holger Kohnen'
14
+ AUTHOR_EMAIL = 'h.kohnen@gmail.com'
15
+ GEM_VERSION = '1.0.0'
16
+ HOMEPAGE = "http://#{TITLE}.rubyforge.org/"
17
+ PROJECT_NAME = TITLE
18
+
19
+ task :default => :test
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.test_files = FileList[
23
+ 'test/test*.rb'
24
+ ]
25
+ t.warning = true
26
+ t.verbose = false
27
+ end
28
+
29
+ desc 'Generate RDoc'
30
+ Rake::RDocTask.new do |task|
31
+ task.main = 'README'
32
+ task.title = TITLE
33
+ task.rdoc_dir = 'doc'
34
+ task.options << "--line-numbers" << "--inline-source"
35
+ task.rdoc_files.include('README', 'lib/**/*.rb')
36
+ end
37
+
38
+ specification = Gem::Specification.new do |s|
39
+ s.name = TITLE
40
+ s.summary = DESCRIPTION
41
+ s.version = GEM_VERSION
42
+ s.author = AUTHOR
43
+ s.description = DESCRIPTION
44
+ s.homepage = HOMEPAGE
45
+ s.rubyforge_project = PROJECT_NAME
46
+
47
+ s.has_rdoc = true
48
+ s.extra_rdoc_files = ['README']
49
+ s.rdoc_options <<
50
+ '--title' << TITLE << '--main' << 'README' << '--line-numbers' << "--inline-source"
51
+
52
+ s.email = AUTHOR_EMAIL
53
+ s.files = FileList['{lib,test}/**/*.rb', '[A-Z]*$', 'Rakefile'].to_a
54
+ s.add_dependency('open4')
55
+ s.add_dependency('expectations')
56
+ end
57
+ Rake::GemPackageTask.new(specification) do |package|
58
+ package.need_zip = false
59
+ package.need_tar = false
60
+ end
61
+
62
+ desc "Upload RDoc to RubyForge"
63
+ task :publish_rdoc do
64
+ Rake::Task[:rdoc].invoke
65
+ Rake::SshDirPublisher.new(
66
+ "holgerkohnen@rubyforge.org",
67
+ "/var/www/gforge-projects/#{PROJECT_NAME}", "doc").upload
68
+ end
@@ -0,0 +1,132 @@
1
+ require 'open4'
2
+
3
+ module Shell
4
+
5
+ class << self
6
+ # Shorthand for Shell::Executer#execute. For +options+ see: Shell::Executer.new.
7
+ def execute(command, options={}, &block)
8
+ Executer.new(options).execute(command, &block)
9
+ end
10
+
11
+ # Shorthand for Shell::Executer#execute!. For +options+ see: Shell::Executer.new.
12
+ def execute!(command, options={}, &block)
13
+ Executer.new(options).execute!(command, &block)
14
+ end
15
+ end
16
+
17
+ class Executer
18
+ # Holds last executed commands standard output if executed without block.
19
+ attr_reader :stdout
20
+
21
+ # Holds last executed commands error output.
22
+ attr_reader :stderr
23
+
24
+ # Configuration options:
25
+ # * <tt>:mode</tt> - Specifies the output mode.
26
+ # Can be <tt>:chars</tt> or <tt>:lines</tt>.
27
+ # In lines-mode every line of the output is passed to the block.
28
+ # In chars-mode every char of the output is passed to the block.
29
+ # (default is: <tt>:lines</tt>)
30
+ def initialize(options={})
31
+ @options = {
32
+ :mode => :lines # :chars|:lines <- output mode
33
+ }.merge(options)
34
+
35
+ @reader = (@options[:mode] == :chars) ? proc {|s| s.getc } : proc {|s| s.gets }
36
+ @mutex = Mutex.new
37
+
38
+ @stdout = ''
39
+ @stderr = ''
40
+ @success = nil
41
+ end
42
+
43
+ # :call-seq: execute(command) -> Shell::Executer
44
+ # execute(command) {|stdout| ...} -> Shell::Executer
45
+ # execute(command) {|stdout, stderr| ...} -> Shell::Executer
46
+ #
47
+ # Executes <tt>command</tt> and returns after execution.
48
+ #
49
+ # In the first form(<tt>execute(command)</tt>) the +command+ ist executed
50
+ # and the +stdout+, +stderr+ and +success+ attributes are populated.
51
+ #
52
+ # In the second and third form the +stdout+ attribute is *not* populated. The +stdout+
53
+ # is passed to the specified block instead. The block is invoked for
54
+ # every char or line of output depending on the specified <tt>options</tt>.
55
+ #
56
+ # In the second form only the stdout is passed to the block.
57
+ #
58
+ # In the third form either stdout or stderr is filled. The opposite is allways nil.
59
+ # # Prefix output of rsync
60
+ # Shell::Executer.new.execute('rsync -avz /source/ /dest/') do |stdout, stderr|
61
+ # if stderr
62
+ # print 'ERR> ' + stderr
63
+ # else
64
+ # print 'OUT> ' + stdout
65
+ # end
66
+ # end
67
+ def execute(command, &block)
68
+ @stdout = ''
69
+ @stderr = ''
70
+ @success = nil
71
+
72
+ begin
73
+ threads = []
74
+ pid, stdin, stdout, stderr = Open4::popen4(command)
75
+ [[:stdout, stdout], [:stderr, stderr]].each do |type, stream|
76
+ threads << Thread.new(type, stream) do |type, stream|
77
+ Thread.current.abort_on_exception = true
78
+ while data = @reader.call(stream)
79
+ data = ((@options[:mode] == :chars) ? data.chr : data)
80
+ @mutex.synchronize do
81
+ if type == :stdout
82
+ if block_given?
83
+ if block.arity == 2
84
+ yield data, nil
85
+ else
86
+ yield data
87
+ end
88
+ else
89
+ @stdout += data
90
+ end
91
+ else
92
+ yield nil, data if block_given? && block.arity == 2
93
+ @stderr += data
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ ignored, status = Process::waitpid2 pid
101
+ @success = (status.exitstatus == 0)
102
+
103
+ ensure
104
+ threads.each(&:join)
105
+ stdin.close if stdin
106
+ stdout.close if stdout
107
+ stderr.close if stderr
108
+ end
109
+
110
+ self
111
+ end
112
+
113
+ # :call-seq: execute!(command) -> Shell::Executer
114
+ # execute!(command) {|stdout| ...} -> Shell::Executer
115
+ # execute!(command) {|stdout, stderr| ...} -> Shell::Executer
116
+ #
117
+ # Same as execute, but throws <tt>RuntimeError</tt> containing stderr if <tt>command</tt>
118
+ # exited without success.
119
+ def execute!(command, &block)
120
+ execute(command, &block)
121
+ raise stderr unless success?
122
+ self
123
+ end
124
+
125
+ # Returns true if the last command was executed successfully.
126
+ def success?
127
+ @success
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,50 @@
1
+ require 'expectations'
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/shell/executer.rb'))
3
+
4
+ Expectations do
5
+
6
+ expect 'stdout' do
7
+ Shell::Executer.new.execute('echo stdout').stdout.strip
8
+ end
9
+
10
+ expect 'stderr' do
11
+ Shell::Executer.new.execute('echo >&2 stderr').stderr.strip
12
+ end
13
+
14
+ expect true do
15
+ Shell::Executer.new.execute('echo 42').success?
16
+ end
17
+
18
+ expect false do
19
+ Shell::Executer.new.execute('ls /does_not_exist').success?
20
+ end
21
+
22
+ expect RuntimeError do
23
+ Shell::Executer.new.execute!('ls /does_not_exist')
24
+ end
25
+
26
+ expect ["std\n", "out\n"] do
27
+ r = []
28
+ Shell::Executer.new.execute('echo std; echo out') do |stdout, stderr|
29
+ r << stdout
30
+ end
31
+ r
32
+ end
33
+
34
+ expect ["4", "2"] do
35
+ r = []
36
+ Shell::Executer.new(:mode => :chars).execute('echo -n 42') do |stdout, stderr|
37
+ r << stdout.strip
38
+ end
39
+ r
40
+ end
41
+
42
+ expect 'stdout' do
43
+ Shell.execute('echo stdout').stdout.strip
44
+ end
45
+
46
+ expect RuntimeError do
47
+ Shell.execute!('ls /does_not_exist')
48
+ end
49
+
50
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shell-executer
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Holger Kohnen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-30 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: open4
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: expectations
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Shell::Executer provides an easy and robust way to execute shell commands. The stdout and stderr data can be read completly after the execution or in chunks during the execution.
36
+ email: h.kohnen@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README
43
+ files:
44
+ - lib/shell/executer.rb
45
+ - test/test_executer.rb
46
+ - Rakefile
47
+ - README
48
+ has_rdoc: true
49
+ homepage: http://shell-executer.rubyforge.org/
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --title
53
+ - shell-executer
54
+ - --main
55
+ - README
56
+ - --line-numbers
57
+ - --inline-source
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project: shell-executer
75
+ rubygems_version: 1.3.1
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: Shell::Executer provides an easy and robust way to execute shell commands. The stdout and stderr data can be read completly after the execution or in chunks during the execution.
79
+ test_files: []
80
+