bemfica has asked for the wisdom of the Perl Monks concerning the following question:

I would like to pipe a list of files to a perl script (the result of a 'find' command) and then have the user respond with a yes or no to a prompt for action on files that match certain conditions.

I am having trouble with STDIN. I use it to read the list of files and then cannot use to prompt the user... Is there a way to close and re-open STDIN (or something of that nature)?

Many thanks.

A.

Replies are listed 'Best First'.
Re: piped AND prompted input
by graff (Chancellor) on Mar 13, 2005 at 05:13 UTC
    This is the sort of thing that Term::ReadLine is for. Here's a simple example -- try piping the output of "find" or "ls" to this script:
    #!/usr/bin/perl use strict; use Term::ReadLine; my @list = <>; # get pipeline input print scalar @list, " file names found\n"; my $term = new Term::ReadLine; my $prmt = "would you like to see the next file name? [y] "; while ( defined( $_ = $term->readline($prmt))) { last if ( /^n/i ); print shift @list; }
    The module takes care of keeping terminal input separate from pipeline/stdin input. If you want, you should be able to interleave the "<>" reads and the "$term->readline" prompted inputs (e.g. read some file names, and when one looks interesting, prompt the user for something). But unless the pipeline input is expected to get really huge, I'd prefer reading it all first, then going over the list in memory.

    update: Another common approach, which might save the user some command line typing (and keep your script simple yet flexible) is to open your input pipeline command from inside the script:

    ... open( FIND, '-|', 'find', $basedir, @opts ) or die "find: $!"; while (<FIND>) { if ( theres_something_about( $_ )) { print "What do you want with $_ ? "; chomp ( my $desired_outcome = <> ); #(fixed missing close-pa +ren) do_something_to( $_, $desired_outcome ); } } close FIND;
    Args that the user would have given to the upstream process could just as well be included among your own script's @ARGV and passed to the pipeline open.

    one more update: (geez, I seem to do this a lot) There are good reasons for using the "-print0" option on the "find" command, and setting $/ (input_record_separator in perl) to "\x0" when reading input from "find ... -print0". It can happen that file names in a unix file system might contain unexpected characters (e.g. line-feed). Yes, really.

Re: piped AND prompted input
by Zaxo (Archbishop) on Mar 13, 2005 at 03:33 UTC

    You can open /dev/tty to some new handle for the user responses. open my $tty, '<', '/dev/tty' or die $!;

    After Compline,
    Zaxo

Re: piped AND prompted input
by manav (Scribe) on Mar 13, 2005 at 10:04 UTC
    #!/usr/bin/perl use strict ; use warnings ; open(CMDOPT, "find /path -type d | ") or die("$! : cant execute find") + ; while(<CMDOPT>) { print "Directory found : $_" ; ##do something with the directory here..... }
    The find here can be any command. A trailing pipe '|' makes perl pipe the output of command into your script as input(with filehandle specified). A leading pipe before the command will make any output written by your Perl code to the filehandle appear as standard input to the command.

    Oh!! and you can display user messages using STDOUT and accept user responses thru STDIN in the loop.....

    Manav
Re: piped AND prompted input
by chas (Priest) on Mar 13, 2005 at 03:37 UTC
    You could do something like:
    print "Enter some files (on one line):\n"; $files=<>; @files=split $files; print "@files\n"; print "What is your favorite color?__"; $response=<>; print $response,"\n";

    chas
    (*Update*: Sorry, I misunderstood what you wanted to do; this wasn't a good answer...I thought you just wanted to read the files from STDIN and then read in some other responses; I overlooked the "piping".)