kj said:
The complication is not entirely obvious to me,
As long as only one part of the code is being clever, you are OK.
But when many parts of the code are being clever, they start tripping
over each other. IO::Unread hiddenly ties and unties file handles (at
least in some situations), so if the whatever you returning the handle to
also, either now or in the future, wants to tie it or get the fd and pass
that out to some other program, it will fail. That is the complexity. I
usually consider things like IO::Unread and Tie::File to be emergency
life-support methods. If I need them, it probably means the code isn't
well-designed for the task that it is currently being used for, and I use
those modules to keep the old code limping along while I'm busy redesigning
and rewriting.
but at any rate
well, I'm open to suggestions...
Based on your last code, if I understand what you are doing and if you
fix the $err thing and make the changes that go with that, you don't need
to read the line from $rdr at all.
Just call eof($rdr) right off the bat. If it returns true, that means
the program exited (or at least closed its own stdout) without producing
any output on stdout. So then you read $err (or *ERR), plus you can wait
on $pid and see what $? is. Based on that, you decide if it was a
error-free empty output, or a error-caused empty output, and either die, or
return the (already at eof, but who cares) $rdr. If eof($rdr) returns
false, it means that at least something was printed to stdout, so in your
model that would mean there is no error, so return the file handle. This
could deadlock if the error message is more than one buffer in size, but as
you say it is can only ever be exactly zero or one lines, that seems
unlikely.
Another issue is that, if this subroutine is invoked many times during
the lifetime of the program, you need a way to wait on the child or
you will accumulate zombies.
Your idea of saving the entire
output of the command to a file is unpalatable because this output
*can* be huge and the function downstream may need only the first
few hundred lines of this huge output. It just so happens that,
by far, the most common possible outcomes for '/some/command' are
either success with a many lines of output or immediate failure
with a one-line error message.
Even if the alternatives besides those two are very rare, do they still
need to be dealt with? I've often painted myself into a corner by using
a method that made it easy to deal with 99.99% but then made it nearly
impossible to deal with the other 0.01%. So now I'm very leery about
things that look like they are starting down that road.
Hence, the idea of checking for
eof after the first line happens to work in this special case.
In your most recent code as it is (i.e. not corrected for the $err thing),
you will report an error if there the program has no error and produces
exactly one line of output. Since you previously said that it is legal
to produce exactly zero lines of output on non-error conditions, I suspect
it is also legal to produce exactly one. So you may falsely be reporting
errors.
But the downstream function requires a filehandle (as opposed to
a pair of handle and string), hence the business with unread.
As I said, I am somewhat skeptical if this approach is to be used as a base
for future growth and evolution. However, if you (correctly) suspect that
your code will not keep evolving in new directions once you have this
particular issue worked out, then I guess this is of lesser concern.
<the rest snipped to ease my pain>
To say that there's egg on my face would be an understatement...
I had in fact read that in the docs not too long ago, and (since
it did not make much sense at the time) I immediately forgot it.
Thanks for the reminder.
The IPC::Open3 behavior is so incredibly counter-intuitive that that
part of the docs just don't sink in the first few times you get bit by it.
It doesn't help that the docs themselves provide absurd examples:
my($wtr, $rdr, $err);
$pid = open3($wtr, $rdr, $err,
'some cmd and args', 'optarg', ...);
Why the heck go to the effort of declaring $err and passing it in,
if the net effect is identical to:
my($wtr, $rdr);
$pid = open3($wtr, $rdr, undef,
'some cmd and args', 'optarg', ...);
That is highly misleading.
Xho