def execute(command, redirects = {})
$session_command = command if @debug
raise(PipeError, command) unless ready?
clear
rerr = redirects[:e] || redirects[:err] || redirects[:stderr] ||
redirects['stderr'] || redirects['e'] || redirects['err'] ||
redirects[2] || redirects['2']
rout = redirects[:o] || redirects[:out] || redirects[:stdout] ||
redirects['stdout'] || redirects['o'] || redirects['out'] ||
redirects[1] || redirects['1']
cmd = Command::new command.to_s
history << cmd if track_history
mutex = Mutex::new
err = {
:io => stderr,
:cmd => cmd.err,
:name => 'stderr',
:begin => false,
:end => false,
:begin_pat => cmd.begin_err_pat,
:end_pat => cmd.end_err_pat,
:redirect => rerr,
:proc => errproc,
:yield => lambda{|buf| yield(nil, buf)},
:mutex => mutex,
}
out = {
:io => stdout,
:cmd => cmd.out,
:name => 'stdout',
:begin => false,
:end => false,
:begin_pat => cmd.begin_out_pat,
:end_pat => cmd.end_out_pat,
:redirect => rout,
:proc => outproc,
:yield => lambda{|buf| yield(buf, nil)},
:mutex => mutex,
}
begin
threads << Thread::new { send_command cmd }
main = Thread::current
exceptions = []
[err, out].each do |iodat|
threads <<
Thread::new(iodat, main) do |iodat, main|
loop do
main.raise(PipeError, command) unless ready?
main.raise ExecutionError, iodat[:name] if iodat[:end] and not iodat[:begin]
break if iodat[:end] or iodat[:io].eof?
line = iodat[:io].gets
buf = nil
case line
when iodat[:end_pat]
iodat[:end] = true
if((m = %r/(.+)__CMD/o.match(line)) and (pre = m[1]))
buf = pre
end
when iodat[:begin_pat]
iodat[:begin] = true
else
next unless iodat[:begin] and not iodat[:end]
buf = line
end
if buf
iodat[:mutex].synchronize do
iodat[:cmd] << buf
iodat[:redirect] << buf if iodat[:redirect]
iodat[:proc].call buf if iodat[:proc]
iodat[:yield].call buf if block_given?
end
end
end
true
end
end
ensure
begin
while((t = threads.shift))
t.join
raise ExecutionError, 'iodat thread failure' unless t.value
end
rescue => e
exceptions << e
retry unless threads.empty?
ensure
unless exceptions.empty?
meta_message = '<' << exceptions.map{|e| "#{ e.message } - (#{ e.class })"}.join('|') << '>'
meta_backtrace = exceptions.map{|e| e.backtrace}.flatten
raise ExecutionError, meta_message, meta_backtrace
end
end
end
[err, out].each do |iodat|
raise ExecutionError, iodat[:name] unless iodat[:begin] and iodat[:end]
end
get_status if respond_to? :get_status
out = err = iodat = nil
return [cmd.out, cmd.err]
end