Read from keyboard

T

* Tong *

Hi,

Is there any way to read from keyboard/tty instead of STDIN?

My script process STDIN, while some time it need user interaction through
the keyboard, leaving the STDIN intact.

I've tried the getc(), and Term::ReadKey. However, they all seems to read
from STDIN. Please help.

Thanks
 
J

jl_post

* Tong * said:
Is there any way to read from keyboard/tty instead of STDIN?

My script process STDIN, while some time it need user interaction through
the keyboard, leaving the STDIN intact.

I've tried the getc(), and Term::ReadKey. However, they all seems to read
from STDIN. Please help.


Dear Tong,

This may not be the answer you're looking for, but did you try
reading all of STDIN first BEFORE requiring user interaction?

For example, if you wanted to ask a user his/her name, you might try
something like this:

my @allLinesFromSTDIN = <>;
print "What is your name? ";
my $name = <STDIN>; # don't forget to chomp() !

The first line reads all immediate lines from STDIN into an array,
leaving all subsequent reads from STDIN free to read from user input.
You are then free to process all the immediate STDIN lines later with
code like:

foreach (@allLinesFromSTDIN) # instead of "foreach(<>)"
{
# The $_ holds a line of input.
# Process $_ here...
}

I hope this helps, Tong.

-- Jean-Luc
 
C

Chris Mattern

* Tong * said:
Hi,

Is there any way to read from keyboard/tty instead of STDIN?

Open /dev/tty and read from it.
My script process STDIN, while some time it need user interaction through
the keyboard, leaving the STDIN intact.

I've tried the getc(), and Term::ReadKey. However, they all seems to read
from STDIN. Please help.

Thanks

--
Christopher Mattern

"Which one you figure tracked us?"
"The ugly one, sir."
"...Could you be more specific?"
 
J

Josef Moellers

Dear Tong,

This may not be the answer you're looking for, but did you try
reading all of STDIN first BEFORE requiring user interaction?

For example, if you wanted to ask a user his/her name, you might try
something like this:

my @allLinesFromSTDIN = <>;
print "What is your name? ";
my $name = <STDIN>; # don't forget to chomp() !

The first line reads all immediate lines from STDIN into an array,
leaving all subsequent reads from STDIN free to read from user input.
You are then free to process all the immediate STDIN lines later with
code like:

foreach (@allLinesFromSTDIN) # instead of "foreach(<>)"
{
# The $_ holds a line of input.
# Process $_ here...
}

I hope this helps, Tong.

No, it won't. You did try your solution? What happened?
I tried, and it didn't work.

When you have read all of STDIN, any subsequent read will return EOF.
There is no magic that re-opens any terminal the process might (or might
not) be connected to.
Imagine what happens if you run a program in the background which has
STDIN redirected to a file and which reads until EOF is detected and
then more! According to your logic, it will start to read from the
keyboard, completely confusing everything.

The direct solution to Tong's question is operating system dependent.
Unter Unix/Linux, Chris' suggestion of (re-) opening "/dev/tty" is
obviously a valid solution (afair, this is how programs like "cpio" work
when requiring user interaction). Under another OS, (re-) opening "CON:"
might work.
IMHO a much better solution is to avoid redirection of STDIN and require
the input file to be passed as an argument. It should even work with the
often used '-' as a command-line argument, as this (re-) opens STDIN,
which is what you want in that case, however, making the mixed
file-/user-input impossible.

Josef
 
M

Martin Kissner

Josef Moellers wrote :
IMHO a much better solution is to avoid redirection of STDIN and require
the input file to be passed as an argument.

I thought about this solution, too.
But if STDIN is reading from a pipe this will not be possible AFAIK.
If there is a way to handle this, I would be interested in how.

Best Regards
Martin
 
J

jl_post

input.

Josef Moellers replied:
No, it won't. You did try your solution? What happened?
I tried, and it didn't work.

Hmmm... it appears you're right. I just tried it with feeding input
from a pipe, and it didn't work, just as you said.

I did have a solution, however. But instead of piping the input in
I specified the file (with the input) as a parameter to be read in with
Perl's diamond operator ("<>"). Since the diamond operator can be used
to read STDIN and to open files specified at the command line, I always
thought they were functionally interchangeable. But as you explained,
if the diamond operator is used to read STDIN, any subsequent read of
STDIN will return EOF.

The reason I tested it by reading the input from a file (instead of
from a pipe) was because I've sometimes had problems with Perl running
under Win32 reading from a pipe (but more on that later in this
post...).
The direct solution to Tong's question is operating system
dependent. Unter Unix/Linux, Chris' suggestion of
(re-) opening "/dev/tty" is obviously a valid solution (afair,
this is how programs like "cpio" work when requiring user
interaction). Under another OS, (re-) opening "CON:"
might work.

For the record, I tested Win32 ActiveState Perl with both "CON" and
"CON:" and they both seem to work.
IMHO a much better solution is to avoid redirection of STDIN and
require the input file to be passed as an argument.

I agree with you there, as that's what I used to test my sample
data... :)

As I mentioned earlier, I found a problem reading piped input on
Win32. You can reproduce this problem by creating a short, one-line
file named "cat.pl" that consists of the line:

print <>;

Then type, at the Win32-DOS prompt:

cat.pl < cat.pl

You'd think it would print out its contents, right? Instead, I get
nothing (I see the DOS prompt right away).

Today I figured out that I can "fix" this problem by explicitly
calling the Perl interpreter, like this:

perl cat.pl < cat.pl

This works as it should.

So here's a question: Why does omitting "perl" prevent the input
from being read in correctly? I'm sure this is a Win32-DOS-MS issue
and not a Perl bug, but I'm not absolutely certain.

Whatever is causing this, this behavoir is the reason I avoided
testing my original solution with actual piped input (as it seemed
unpredictable at the time), and instead opted to test with the file
specified at the command line, thinking that it would make no
difference. I was wrong, of course, about it making no difference.

(For those interested, my "perl -v" output is:

This is perl, v5.8.0 built for MSWin32-x86-multi-thread
(with 1 registered patch, see perl -V for more detail)
Copyright 1987-2002, Larry Wall
Binary build 805 provided by ActiveState Corp.
http://www.ActiveState.com
Built 18:08:02 Feb 4 2003
)

Anyway, Josef, thanks for pointing out my error.

-- Jean-Luc
 
G

GreenLeaf

As I mentioned earlier, I found a problem reading piped input on
Win32. You can reproduce this problem by creating a short, one-line
file named "cat.pl" that consists of the line:

print <>;

Then type, at the Win32-DOS prompt:

cat.pl < cat.pl

You'd think it would print out its contents, right? Instead, I get
nothing (I see the DOS prompt right away).

Why should one expect so? If .pl extension is not associated with perl
but with, say, your editor, it's supposed to open cat.pl in your editor.
If it's associated with perl, the following is applicable:

Correct me if I'm wrong, I don't know much on _exactly_ how '<' is
supposed to work in Unix, but I guess this is not a 'pipe' in Windows.
They officially acknowledge the word pipe only for '|'.

: Reads the command input from a file, instead of reading input
from the keyboard.

| : Reads the output from one command and writes it to the input of
another command. *Also known as a pipe*.

http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx
</quote:ms>

See, their redirection operator is supposed to read from _files_. When
_MS_ says _file_, it's not always safe to assume that it would work for
other devices, IMHO. Sometimes they work similarly, as for the case of
CON.
Today I figured out that I can "fix" this problem by explicitly
calling the Perl interpreter, like this:

perl cat.pl < cat.pl

This works as it should.


Just for the record, so does

perl cat.pl | perl cat.pl

but not

perl cat.pl < perl cat.pl

for obvious reasons :)

<output>
------------------------------------------
C:\temp>perl cat.pl |perl cat.pl
this
is
a
test
^Z (typed EOF here)
this
is
a
test
C:\temp>
-------------------------------------------
C:\temp>perl cat.pl < perl cat.pl
The system cannot find the file specified.
</output>

This is because it's looking for a _file_ named perl. Well, in *my*
case, Linux produced exactly the same behavior.

<output>
[greenleaf@lah greenleaf]$ echo 'print <>' > cat.pl
[greenleaf@lah greenleaf]$ perl cat.pl | perl cat.pl
this
is a
test. (typed EOF here)
this
is a
test.
[greenleaf@lah greenleaf]$ perl cat.pl < perl cat.pl
bash: perl: No such file or directory
So here's a question: Why does omitting "perl" prevent the input
from being read in correctly?

I think this is answered above. 'Opening' a non-executable in windows
means opening in it the designated application. Just curious: what's
your default association for file type .pl? If it's the editor and if
you had already opened the file, it may be the case that you did not
notice it.
I'm sure this is a Win32-DOS-MS issue
and not a Perl bug, but I'm not absolutely certain.
Exactly.

(For those interested, my "perl -v" output is:

Apparently your perl is well alive and kicking ;-). The issue (not
a problem anymore I guess) is clearly about redirection. :)

HTH,
sat.
 
J

jl_post

GreenLeaf replied:
I think this is answered above. 'Opening' a non-executable in windows
means opening in it the designated application. Just curious: what's
your default association for file type .pl? If it's the editor and if
you had already opened the file, it may be the case that you did not
notice it.


My default association for file type .pl is perl (specifically,
D:\Perl\bin\perl.exe). The script I showed you definitely runs. It's
hard to tell as that one-line script, so let me propose this slightly
longer script, named "cat.pl":


# Code:
print "Running $0 ...\n";
print <>;


Now, when I type:

cat.pl cat.pl

at the prompt I get the expected output:

Running path\cat.pl ...
# Code:
print "Running $0 ...\n";
print <>;

But when I type:

cat.pl < cat.pl

I just get:

Running path\cat.pl ...

and when I type this (using DOS "type" which is like Unix "cat"):

type cat.pl | cat.pl

I get the output:

Running path\cat.pl ...
The process tried to write to a nonexistent pipe.

I'm clueless as to why I get the error message of a nonexistent pipe.

When I place "perl" in front of cat.pl, like this:

perl cat.pl < cat.pl

or:

type cat.pl | perl cat.pl

I get the expected output of:

Running cat.pl ...
# Code:
print "Running $0 ...\n";
print <>;

Obviously, even without "perl" at the command line the Perl script
is still running (otherwise it wouldn't print "Running ..."), but it
seems to have problems reading in standard input when read from a pipe
or from re-direction. Of course, this problem only happens under
Win32-DOS (Unix flavors don't seem to have this problem).

This is an odd problem that can be easily "fixed" by placing "perl"
in front of the script to be executed, so it's not a huge show-stopper
problem. But still, it's nice to know about it in case it ever comes
up.

If anyone knows why this happens (that is, omitting "perl" under a
Win32-DOS command-line causes a script to have problems reading STDIN
from a pipe), feel free to let me know.

-- Jean-Luc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,169
Messages
2,570,917
Members
47,458
Latest member
Chris#

Latest Threads

Top