Piloting processes through standard I/O in Ruby Jan 25 th  2012 Muriel Salvan Open Source Lead developer and architect X-A...
The needs <ul>Run external  processes from Ruby Access their  stdin/out/err in real time  (follow their execution tightly)...
Solutions tested <ul><li>IO::popen
Kernel::open
systemu
PTY::spawn
ChildProcess
Open3::popen3
ProcessPilot
Aruba  (used with  Cucumber ) </li></ul>
External test program <ul><li>This is Line 1, before sleep 1
This is Line 2, after sleep 1
Enter string 1:  My String 1
String 1 entered: My String 1
This is Line 1 on STDERR, before sleep 1
This is Line 2 on STDERR, after sleep 1
Enter string 2 from STDERR:   My String 2
String 2 entered: My String 2
This is Line 3, before sleep 1
This is Line 4, after sleep 1 </li></ul>Output : Output on STDOUT only Output : Output on STDERR only Output : Output for ...
IO::popen IO . popen ( 'ruby test.rb' ,  'r+' )   do   | io | io . write ( &quot;My String 1 n My String 2 n &quot; ) io ....
IO::popen <ul><li>stdin must be closed before accessing stdout =>  No real time
stderr  support only for  Ruby > 1.9
Upcoming SlideShare
Loading in …5
×

Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012

1,681 views
1,563 views

Published on

Presented at the Ruby Drink-up of Sophia Antipolis on the 25th of January 2012 by Muriel Salvan (@MurielSalvan).

Published in: Technology, Health & Medicine
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,681
On SlideShare
0
From Embeds
0
Number of Embeds
52
Actions
Shares
0
Downloads
8
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Piloting processes through std IO at the Ruby Drink-up of Sophia, January 2012

  1. 1. Piloting processes through standard I/O in Ruby Jan 25 th 2012 Muriel Salvan Open Source Lead developer and architect X-Aeon Solutions http://x-aeon.com
  2. 2. The needs <ul>Run external processes from Ruby Access their stdin/out/err in real time (follow their execution tightly) Work on both Unix and Windows Work also with external Ruby programs </ul>
  3. 3. Solutions tested <ul><li>IO::popen
  4. 4. Kernel::open
  5. 5. systemu
  6. 6. PTY::spawn
  7. 7. ChildProcess
  8. 8. Open3::popen3
  9. 9. ProcessPilot
  10. 10. Aruba (used with Cucumber ) </li></ul>
  11. 11. External test program <ul><li>This is Line 1, before sleep 1
  12. 12. This is Line 2, after sleep 1
  13. 13. Enter string 1: My String 1
  14. 14. String 1 entered: My String 1
  15. 15. This is Line 1 on STDERR, before sleep 1
  16. 16. This is Line 2 on STDERR, after sleep 1
  17. 17. Enter string 2 from STDERR: My String 2
  18. 18. String 2 entered: My String 2
  19. 19. This is Line 3, before sleep 1
  20. 20. This is Line 4, after sleep 1 </li></ul>Output : Output on STDOUT only Output : Output on STDERR only Output : Output for interactive tests only Input : Input taken from STDIN sleep 1 sleep 1 sleep 1 Input from stdin Input from stdin
  21. 21. IO::popen IO . popen ( 'ruby test.rb' , 'r+' ) do | io | io . write ( &quot;My String 1 n My String 2 n &quot; ) io . close_write puts &quot;stdout: #{io.read}” end
  22. 22. IO::popen <ul><li>stdin must be closed before accessing stdout => No real time
  23. 23. stderr support only for Ruby > 1.9
  24. 24. stderr support does not work on Windows
  25. 25. Does not work with Ruby programs that don't explicitly turn off stdout internal cache </li></ul>
  26. 26. Kernel::open Kernel :: open ( '|ruby test.rb', 'r+' ) do | io | puts io . gets # stdout Line 1 puts io . gets # stdout Line 2 puts io . read ( 31 ) # stdout Prompt sleep 1 io . write ( &quot;My String 1 n &quot; ) puts io . gets # stdout String 1 sleep 1 io . write ( &quot;My String 2 n &quot; ) puts io . gets # stdout Line 3 puts io . gets # stdout Line 4 end
  27. 27. Kernel::open <ul><li>stdin and stdout can be written and read in real time </li></ul><ul><li>No support for stderr
  28. 28. Does not work with Ruby programs that don't explicitly turn off stdout internal cache </li></ul>
  29. 29. systemu require 'systemu' status, stdout, stderr = systemu ( 'ruby test.rb' , 0 => &quot;My String 1 n My String 2 n &quot; ) puts &quot;stdout: #{stdout}&quot; puts &quot;stderr: #{stderr}&quot;
  30. 30. systemu <ul><li>Handle stderr </li></ul><ul><li>Does not work on Windows
  31. 31. stdin must be given to the process at startup time => No real time
  32. 32. stdout/err can only be accessed when process has exited => No real time </li></ul>
  33. 33. PTY::spawn require 'pty' PTY ::spawn ( 'ruby test.rb' ) do | stdout, stdin, pid | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( &quot;My String 1 n &quot; ) puts stdout. gets # Extra for input echoed puts stdout. gets # stdout String 1 puts stdout. gets # Should be on stderr puts stdout. gets # Should be on stderr puts stdout. read ( 43 ) # Should be on stderr sleep 1 stdin. write ( &quot;My String 2 n &quot; ) puts stdout. gets # Extra for input echoed puts stdout. gets # Should be on stderr puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  34. 34. PTY::spawn <ul><li>stdin and stdout can be written and read in real time </li></ul><ul><li>Does not exist on Windows
  35. 35. Mix stdout and stderr in the same IO => No stderr support
  36. 36. Echoes all input from stdin to stdout </li></ul>
  37. 37. ChildProcess require 'childprocess' require 'tempfile' lProcess = ChildProcess. build ( 'ruby' , 'test.rb' ) lProcess. duplex = true Tempfile . open ( 'stdout' ) do | oStdOUT | lProcess. io . stdout = oStdOUT Tempfile . open ( 'stderr' ) do | oStdERR | lProcess. io . stderr = oStdERR lProcess. start stdout = File . open ( oStdOUT. path , 'r' ) stderr = File . open ( oStdERR. path , 'r' ) stdin = lProcess. io . stdin waitfor ( stdout, 'This is Line 2' ) # Need active wait sleep 1 stdin. write ( &quot;My String 1 n &quot; ) waitfor ( stderr, 'This is Line 2' ) # Need active wait sleep 1 stdin. write ( &quot;My String 2 n &quot; ) waitfor ( stdout, 'This is Line 4' ) # Need active wait end end
  38. 38. ChildProcess <ul><li>All stdin/out/err can be written/read in real time </li></ul><ul><li>Need stdout/err redirections to files on Windows
  39. 39. Need active blocking read on stdout and stderr to wait for next line
  40. 40. Very unstable on native Windows
  41. 41. Does not work with Ruby programs that don't explicitly turn off stdout internal cache </li></ul>
  42. 42. Open3::popen3 require 'open3' Open3 ::popen3 ( 'ruby test.rb' ) do | stdin, stdout, stderr, wait_thr | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( &quot;My String 1 n &quot; ) puts stdout. gets # stdout String 1 puts stderr. gets # stderr Line 1 puts stderr. gets # stderr Line 2 puts stderr. read ( 43 ) # stderr Prompt sleep 1 stdin. write ( &quot;My String 2 n &quot; ) puts stderr. gets # stderr String 2 puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  43. 43. Open3::popen3 <ul><li>All stdin/out/err can be written/read in real time
  44. 44. Works on any OS
  45. 45. No need for files redirection </li></ul><ul><li>Does not work with Ruby programs that don't explicitly turn off stdout internal cache </li></ul>
  46. 46. ProcessPilot require 'processpilot/processpilot' ProcessPilot ::pilot ( 'ruby' , 'test.rb' ) do | stdin, stdout, stderr, childproc | puts stdout. gets # stdout Line 1 puts stdout. gets # stdout Line 2 puts stdout. read ( 31 ) # stdout Prompt sleep 1 stdin. write ( &quot;My String 1 n &quot; ) puts stdout. gets # stdout String 1 puts stderr. gets # stderr Line 1 puts stderr. gets # stderr Line 2 puts stderr. read ( 43 ) # stderr Prompt sleep 1 stdin. write ( &quot;My String 2 n &quot; ) puts stderr. gets # stderr String 2 puts stdout. gets # stdout Line 3 puts stdout. gets # stdout Line 4 end
  47. 47. ProcessPilot <ul><li>All stdin/out/err can be written/read in real time
  48. 48. Works on any OS
  49. 49. Works with all Ruby programs , even if they don't explicitly turn off stdout internal cache
  50. 50. Provides time-out mechanisms in its API </li></ul>
  51. 51. ProcessPilot <ul><li>Time outs: </li></ul>stdout. gets ( :TimeOutSecs => 5 ) <ul><li>Deactivate Ruby cache: </li></ul>ProcessPilot ::pilot ( 'test.rb', :ForceRubyProcessSync => true ) do | stdin, stdout, stderr, childproc | # … end
  52. 52. Aruba When I run `ruby test.rb` interactively And I type &quot;My String 1&quot; And I type &quot;My String 2&quot; Then the stdout should contain : &quot;&quot;&quot; This is Line 1, before sleep 1 This is Line 2, after sleep 1 Enter string 1: String 1 entered: My String 1 This is Line 3, before sleep 1 This is Line 4, after sleep 1 &quot;&quot;&quot; And the stderr should contain : &quot; &quot;&quot; This is Line 1 on STDERR, before sleep 1 This is Line 2 on STDERR, after sleep 1 Enter string 2 from STDERR: String 2 entered: My String 2 &quot;&quot;&quot;
  53. 53. Aruba <ul><li>Handle stderr correctly
  54. 54. Works on any OS
  55. 55. Works with all Ruby programs , even if they don't explicitly turn off stdout internal cache </li></ul><ul><li>All stdin has to be entered at the beginning of the scenario: No real time
  56. 56. No easy way to test for lines sequence between stdout and stderr flows </li></ul>
  57. 57. Links <ul><li>IO::popen
  58. 58. Kernel::open
  59. 59. systemu
  60. 60. PTY::spawn
  61. 61. ChildProcess
  62. 62. Open3::popen3
  63. 63. ProcessPilot
  64. 64. Aruba (used with Cucumber ) </li></ul>This presentation is available under CC-BY license by Muriel Salvan
  65. 65. Q/A

×