Re: Piping Strangeness
Re: Piping Strangeness
- Subject: Re: Piping Strangeness
- From: Sherm Pendley <email@hidden>
- Date: Sat, 30 Nov 2002 15:29:58 -0500
On Saturday, November 30, 2002, at 02:03 PM, mw wrote:
Otherwise, couldn't I just send \n characters at the end of the print
statements to have it flush back?
Yes - that's what I meant by "line-buffered." Every time you send a
newline (\n), Perl outputs whatever has been sent up to that point to
its standard output stream. When the Perl program ends, of course, it
sends whatever is left in its output buffer, newline or not. So, in a
simple 3-line test that sends no newlines, the effect is that no output
gets sent to the parent program until the Perl program ends.
One more question that I forgot to ask the rest of the list. How can I
have
both read and write NSPipes open at the same time?
You can have as many NSPipe instances open as you want - subject to the
system's limit of maximum open file handles of course, which is normally
in the hundreds. You can check that with "ulimit -a" - on my X.I.V
system, it's 256.
It was Perl's line-buffering that was biting you - not a limitation on
NSPipes. Because your Perl program didn't output any newlines, nothing
appeared in its standard output stream until it ended - which didn't
happen until you closed its standard input stream from the parent.
Note that Perl's standard error stream is not buffered - which can lead
to some misleading output, if you're not expecting it. For example:
print ", world!";
print STDERR "Hello";
print "\n";
The above will output "Hello, world!" - which is not quite what you'd
expect at first glance. That's because the "Hello" sent to STDERR is
printed immediately, whereas the output to STDOUT is buffered, and that
buffer isn't flushed until a "\n" is printed.
Suppose, like many folks, you use print statements as a debugging aid
and test your Perl program by running it from a command prompt. If those
debugging messages are sent to STDERR, they can appear in unexpected
spots in your program's output - probably leading to massive confusion
and lots of head-scratching.
On the other hand, if you set $|, like this:
$| = 1;
print ", world!";
print STDERR "Hello";
print "\n";
This prints ", world!Hello", because output to STDOUT is sent
immediately, rather than being buffered and sent line-by-line.
It seems to be quite
cumbersome to have to close one of the pipes every time you want to
perform
a read/write operation. It says in the docs how to go about this by
using a
handle to an NSPipe (and then recommends against doing it) which will
keep
the other pipe (whether that be read or write) from being closed.
I think you're misreading the docs - although that's understandable,
given that you were misled by Perl's buffering behavior.
When you attach an NSPipe instance to an NSTask with (for example)
setStandardInput:, that read end of that NSPipe instance (and *only*
that instance) is closed in the parent task. Similarly, attaching an
NSPipe to a child with setStandardOutput: will close the write end of
that NSPipe instance.
The key part of this is the phrase "that instance." Using
setStandardInput: closes the read end of that pipe because it's a
one-way pipe; the child process' input stream is write-only to the
parent. Similarly, setStandardOutput: closes the write end of that pipe,
because the child process' output stream is read-only to the parent.
Any thoughts on how to get this "handle"?
If you want the child process to read from (or write to) a file, rather
than to the parent process via a pipe, you can open an NSFileHandle and
pass that to setStandardInput: and friends.
What the docs recommend against doing is using pipe() to create a pipe
to the child process, creating an NSFileHandle from that file descriptor
with initWithFileDescriptor:, and passing the resulting NSFileHandle to
NSTask. Opening a pipe to a child task that way will circumvent the
automatic closing of the "wrong" end of the pipe done by NSTask, because
that only happens when NSTask is given an instance of NSPipe, not when
it's given an NSFileHandle instance.
It won't make the pipe two-way, though. All that creating a pipe that
way does is bypass the error checking - pipes are one-way, regardless of
how they're created, and trying to use one the wrong way will produce
errors at best, and a crash at worst.
In other words, if what you want is a pipe - use NSPipe. An NSFileHandle
can be convinced to do the job of a pipe, with the help of some
low-level BSD functions, but you'll be bypassing some sanity checks if
you do it that way, and there's nothing to be gained in return.
sherm--
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.