14

I'm trying to write a perl script that reads piped data, then prompts the user for input based on that data. The following script, prompt_for_action, is what I am trying to do:

#!/usr/bin/perl 

my @hosts = ();
while (<>) {
    my $host = $_;
    $host =~ s/\n//; # strip newlines
    push(@hosts, $host);
}

for my $host (@hosts) {
    print "Do you want to do x with $host ? y/n: ";
    chomp(my $answer = <>);
    print "You said `$answer`.\n";
}

but when I run it there is no waiting for user input, it just blows through without waiting for me to type:

$ echo "test1.example.com
> test2.example.com" | ./prompt_for_action
Do you want to do x with test1.example.com ? y/n: You said ``.
Do you want to do x with test2.example.com ? y/n: You said ``.

If I don't read my data from STDIN...

#!/usr/bin/perl 

my @hosts = ('test1.example.com', 'test12.example.com');

for my $host (@hosts) {
    print "Do you want to do x with $host ? y/n: ";
    chomp(my $answer = <>);
    print "You said `$answer`.\n";
}

then script works fine:

$ ./prompt_for_action 
Do you want to do x with test1.example.com ? y/n: y
You said `y`.
Do you want to do x with test12.example.com ? y/n: n
You said `n`.

Is piping to STDIN and then prompting for user input possible? If so how?

Adam Franco
  • 81,148
  • 4
  • 36
  • 39

3 Answers3

14

On Unix-y systems, you can open the /dev/tty pseudo-file for reading.

while (<STDIN>) {
    print "from STDIN: $_";
}
close STDIN;

# oops, need to read something from the console now
open TTY, '<', '/dev/tty';
print "Enter your age: ";
chomp($age = <TTY>);
close TTY;
print "You look good for $age years old.\n";
mob
  • 117,087
  • 18
  • 149
  • 283
  • 1
    i was able to just close STDIN, then: open STDIN, "<", "/dev/tty", is there a reason you should not do this? It didnt seem to have any side effects as far as I could tell. Obviously doing this kind of 'bravery' programming is not advisable for large programs but small tools should be okay right? – osirisgothra Sep 26 '14 at 18:19
1

As for non-Unix-y systems you can use the findConsole from Term::ReadLine, and then use its output like in mob's answer, e.g. instead of /dev/tty put in the output of findConsole first element.

Example on Windows:

use Term::ReadLine;
while (<STDIN>) {
    print "from STDIN: $_";
}
close STDIN;

# oops, need to read something from the console now
my $term = Term::ReadLine->new('term');
my @_IO = $term->findConsole();
my $_IN = $_IO[0];
print "INPUT is: $_IN\n";
open TTY, '<', $_IN;
print "Enter your age: ";
chomp($age = <TTY>);
close TTY;
print "You look good for $age years old.\n";

outputs:

echo SOME | perl tty.pl
from STDIN: SOME
INPUT is: CONIN$
Enter your age: 12 # you can now enter here!
You look good for 12 years old.
BladeMight
  • 2,670
  • 2
  • 21
  • 35
-2

Once you pipe something through STDIN, your former STDIN (keyboard input) is being replaced by the pipe. So I don't think this is possible.