From: Brian Candler Date: 2003-05-10T16:50:55+09:00 Subject: Re: capture output On Sat, May 10, 2003 at 10:03:39AM +0900, Simon Strandgaard wrote: > BTW: pipe is a unix thing.. How do I make a ruby-sandbox on windows? > I don't own a windows box myself. What issues should I be aware of ??? I don't use Windows either, but Windows does support processes with pipes between them, based on what I've seen posted here. It works as well as you can expect any O.S. feature in Windows to work :-) Even DOS had pipes of a sort: C:\> type autoexec.bat | more (which it implemented by running command 1 outputting to a temporary file, then running command 2 reading from the temporary file) > ruby-in-a-sandbox: Im working on editor project where I "think" I need > this feature (my knowledge about unix-TTYs is minimal). And you need it because you want the vim-like feature: "run this program and capture its output into the editor buffer"? My argument is that the capturing belongs at the point where you run the external program, just as if you were writing your code in C++ directly, not in some framework where you have to jump through hoops with pipes and threads (which are dangerous here) How about adding a FAQ to your tutorial? Here's one I contribute. Q. How do I capture the entire output of an external program into a Ruby string? I tried string = `someprog args` # or %x{someprog args} but anything generated on stderr by that program is still sent to my own program's stderr A. If you don't mind mixing the child's stdout and stderr, you might just get away with using shell redirection: string = `someprog args 2>&1` However this doesn't work under Windows, and if 'someprog' itself doesn't exist, the shell itself will still write to stderr. When you use backticks, Ruby is actually spawning a child (fork/exec) and collecting its stdout using a pipe. You can do this explicitly and collect both stdout and stderr through separate pipes: require 'open3' outstr = errstr = nil inp, out, err = Open3.popen3("ls -l /nonexistent /dev/null") t1 = Thread.new { outstr = out.read } t2 = Thread.new { errstr = err.read } # If you want to send to the child's stdin, write to 'inp' here inp.close t1.join t2.join puts "stdout said: #{outstr.inspect}" puts "stderr said: #{errstr.inspect}" # stdout said: "crw-rw-rw- 1 root wheel 2, 2 May 10 08:26 /dev/null\n" # stderr said: "ls: /nonexistent: No such file or directory\n" This is essentially the same as you'd do if writing C++ to achieve the same goal: create some pipes, fork, attach one end of the pipes to fd 0/1/2 in the child, and exec the target program. In the parent you'd have to read from both output pipes using a select() loop or threads. Ruby's threads are actually implemented using select() behind the scenes. Then you can show how simple it is to embed Ruby in C++ (presumably the same as the 4 or 5 lines you need when embedding Ruby in C), and just deal with these side-issues as they arise. > Another case: many unix-applications is today being started from the > desktop, and no console output is visible. You will need to redirect > rubys output to the applications status-box/log-window. But by the same argument: you will have to redirect all your C++ program's output to the applications status-box/log-window, or else write your C++ program so that it never writes to stdout or stderr. So I don't see how embedded Ruby is any different; it's just a part of your C++ program. There's nothing special about a Ruby script which says it *must* write to stdout or stderr. If you were writing your editor entirely in C++, or entirely in Ruby for that matter, you'd have the same issue. So this isn't really anything specific to embedding Ruby. On a different point, I hope that catching exceptions is an area you have covered or will cover in the tutorial? Regards, Brian.