revdiablo has asked for the wisdom of the Perl Monks concerning the following question:
To start off, I am talking from a unix point of view. I know EOF is not a character, and it doesn't seem to be a unix signal. So, what is it?
The reason I ask, is because I have a program that wants to read two separate streams from STDIN. Example:
perl -e 'print "1:$_" while <>; print "2:$_" while <>;'
When run directly from the terminal, you can type a few lines of input, press ^D, type a few more, and press ^D again. Everything works as expected: the first while loop gets the first stream of input, the second gets the second.
Now, the question is, how to do what the terminal does when ^D is pressed? It's not sending the ^D char. It's catching the ^D keypress, and doing something. I looked in man bash for some guidance, and all it says is:
delete-char (C-d)
Delete the character at point. If point is at the
beginning of the line, there are no characters in the
line, and the last character typed was not bound to
delete-char, then return EOF.
So, apparently bash "return[s] EOF," but what exactly is the EOF that it is returning, and how can I do that from a Perl program?
Any ideas will be appreciated.
Update: clarified a bit
Re: what is EOF and how can I send it? (0 bytes read)
by tye (Sage) on Jun 05, 2004 at 05:16 UTC
|
EOF is when you ask to read some byte(s) and you don't get an error but you get zero bytes read.
Interactively, CTRL-D flushes your input. If you've just flushed your input already (by pressing RETURN which queued up a newline then flushed the input), then flushing again with CTRL-D means that 0 bytes get flushed to the next read request (and so gets interpretted as EOF).
Most other forms of I/O don't support this abillity to flush all the way through to the reader without irreversibly closing the handle.
In other words, you're out of luck... unless you want to use a pseudo-tty...
| [reply] |
|
Ok well I just ran into this post, and I think that the conversation got muddled... the Key is if you're doing a bidirectional pipe to close the Writer stream... Sorry about updating an old post, but its first Google response.
sub pushusers() {
my $host = shift;
my $cmd = sprintf("ssh %s@%s perl",$user,$host);
my $pid = open2(*Reader, *Writer, $cmd);
print Writer "die('got into perl')\n";
close(Writer);
my $output = <Reader>;
print "Got from host ${host} output: ${output}";
exit(0);
}
-Anacreo | [reply] [d/l] |
Re: what is EOF and how can I send it?
by toma (Vicar) on Jun 05, 2004 at 07:02 UTC
|
This is what Expect is for.
The Expect module handles the pseudo-tty details,
so you don't have to!
This program is called 'consumer.pl'. I added a
clue for Expect to use to find the end of the
output. You can do without the ending markers,
you just have to be a little more clever with Expect.
This sample program is similar to yours, in that it
uses multiple input streams. It writes a file
for each stream.
use File::Slurp;
my $out='';
while(<>)
{
$out .= "1:$_";
}
$out .= "end1\n";
write_file("out1.txt", $out);
$out='';
while(<>)
{
$out .= "2:$_";
}
$out .= "end2\n";
write_file("out2.txt", $out);
Here is a program to communicate with it, and
send it two streams of data. The Expect module
sends control-D with the string "\cD".
use Expect;
my $proc= new Expect();
$proc->spawn("./consumer.pl");
$proc->send("hello there\n");
$proc->send("\cD");
$proc->send("this is\n");
$proc->send("\cD");
$proc->expect(1,
[
/end1/ => sub
{
exp_continue;
}
],
[
/end2/ => sub
{
exp_continue;
}
]
);
I'm sure that this code could be better, it
is intended as proof of concept. I have only
used Expect a few times. I find it
difficult but worthwhile.
minor update: pushed the code around a bit
It should work perfectly the first time! - toma
| [reply] [d/l] [select] |
Re: what is EOF and how can I send it?
by virtualsue (Vicar) on Jun 05, 2004 at 06:43 UTC
|
Try the manual page for stty instead of the one for the shell. eof is a function of the terminal device interface, along with backspace (erase), word erase (werase - many people never use this nifty feature anymore now that few of us are ever on a realllly sloooow link to a remote system these days), interrupt (^C usually), stop, quit, suspend etc.
If you want to emulate that behavior (moving to the next loop or whatever in your program after user presses ^D) then you'll have to DIY. However, since your program isn't a terminal input device, you'll probably decide to do something else. :) | [reply] |
Re: what is EOF and how can I send it?
by graff (Chancellor) on Jun 06, 2004 at 15:08 UTC
|
Talking from a unix point of view, I have to ask for yet a little more clarification on this point:
I have a program that wants to read two separate streams from STDIN.
In unix, a data file is also referred to as a "stream". Since your example just uses while <>, I wonder if you're talking about a case where two files (streams) are being presented to the script via ARGV, like this:
some_script.pl file1 file2
If that is what you mean, then you just need to study the explanation given by "perldoc -f eof", which includes the following:
In a "while (<>)" loop, "eof" or "eof(ARGV)" can be used to
detect the end of each file, "eof()" will only detect the end
of the last file. Examples: ...
An example relevant to your post would be:
my $prefix = 1;
while (<>)
{
print "$prefix: $_";
$prefix++ if ( eof );
}
The only other situation I can imagine for the "two streams on STDIN" would be something like this:
cat file1 file2 | some_script.pl
# or something less mundane:
grep "target string" file1 | cat - file2 | some_script.pl
Of course, the whole point of the unix "cat" command is to concatenate two or more streams into a single stream, so in cases like this, the perl script is really only getting one stream, and unless there are reliable clues in the data content to indicate the different sources, the script will have no basis for telling one source from another -- it really is just one stream to the perl script, and there's no way around that.
(update: I should say, the only way around that is to make sure there are reliable clues in the resulting stream -- e.g.:
echo "____STREAM_BOUNDARY____" > bndry
grep "target string" file1 | cat - bndry file2 | script.pl
and you fix script.pl so that it knows to look for the specific string that flags the stream boundary.)
In the earlier case, where the script is given two or more file names in @ARGV, and these are read successively by the diamond operator, perl obviously knows when one file ends and the next must be opened, and "eof" is how you find out about that.
If your notion of "two streams on STDIN" is referring to something other than these cases, then I'm puzzled as to what you could be talking about. | [reply] [d/l] [select] |
|
Sorry for any confusion it might have caused, but I used the diamond operator simply for convenience. Pretend that instead of <>, I had written <STDIN>, and it might make more sense. How to read the streams wasn't the question I had, however. In fact, the reading program was out of my control anyway. I found it odd that it was able to read two separate streams interactively, and I wondered how to write them both to STDOUT. tye's response clarified that bit for me, specifically where he wrote:
flushing again with CTRL-D means that 0 bytes get flushed to the next read request (and so gets interpretted as EOF).
And toma's response explained how I can emulate the terminal's behavior from a non-interactive perl script. Thanks for taking the time to reply, though.
Update: btw, just to pass along a neat little trick, if using bash (or perhaps other shells support this, but only tested on bash) you could avoid the temporary file in your last example with something like:
grep "target string" file1 \
| cat - <(echo "____STREAM_BOUNDARY____") file2 \
| script.pl
| [reply] [d/l] [select] |
|
|