shell-executer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+