Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Pipping a script with prompts into less

by xorl (Deacon)
on Oct 18, 2012 at 17:02 UTC ( [id://999770]=perlquestion: print w/replies, xml ) Need Help??

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

I have this script that does something like:
my $foo = <>; if ($foo eq "bar") { }
Of course the script gives lots of output so when I run it, I want to pipe it into less
[/home/xorl]$ ./myscript.pl | less
This is all well and good until the prompt happens. Then there doesn't seem to be any way to get input to the script without quitting less. However if I do that it kills the script too. Is there a good way to handle a sistuation like this? I tried also redirecting the output to a file, but that has a similar problem.

Edit: Also I'd like it to "work" if I don't pipe it into less as well.

Replies are listed 'Best First'.
Re: Pipping a script with prompts into less (edit script)
by tye (Sage) on Oct 18, 2012 at 17:14 UTC
    sub prompt { my( $prompt, $validator, $reprompt ) = @_; # Close out previous pager (if any): select STDOUT; close PAGER; # Prompt the user for a response: my $response; while( 1 ) { print STDERR $prompt; $response = <STDIN>; die "End of input.\n" if ! defined $response; chomp $response; last if $validator->( $response ); $prompt = $reprompt; } # Run output (until next prompt) through pager: my $pager = $ENV{PAGER} || 'less'; open PAGER, '|-', $pager or die "Can't run $pager: $!\n"; select PAGER; return $response; }

    If you have output that isn't done by bare 'print', then you probably want to instead save and re-open STDOUT to be just another file handle that goes to PAGER. See open for examples of how to do that correctly.

    - tye        

      Thanks!

      Three questions:

      1. What is $validator supposed to be? A ref to a subroutine or something else?
      2. None of the output before the prompt is visible until after the prompt is done, what do I do if I want to see that before the prompt?
      3. What if I'm not piping the script into less? How can I see everything after the prompt?

      My example code:

      print "A"; print "B"; prompt("Question1:", "", "Wrong answer try again:"); print "C"; print "D"; sub prompt { # same as above excpet I commented out the if $validator-> part }

      when I do

      ./myscript.pl | less
      all I see at first is:
      Question1:
      After I type my answer (which I can't see), I'm in less with just
      Question1:
      C
      D
      (END)
      I'd like to be able to see A and B too (and before the prompt)

      Also if I don't pipe the script into less I don't see anything after the prompt (yes I realize that wasn't part of my original question but it should have been).

        The (modified) script is running 'less', so you piping to less as well just gets in the way. If you want to prevent the script from running 'less' sometimes, then you'd need to implement a command-line option or similar to disable that feature.

        To be extra clear, just run the modified script by itself. Don't type "| less" as then you end up with two instances of 'less' running at the same time (and, more importantly, "| less" still gets in the way of you entering information at the prompts, just like before).

        You might want to tell 'less' to (by default) not prompt if no more than a screen-full of text was output before the next prompt ('less -F'). 'less -E' might also be desirable since you'll have multiple "ends" to have to get past in this case (or you might find that either of those make some cases a bit confusing).

        sub prompt { my( $prompt, $validator, $reprompt ) = @_; my $interactive = -t STDOUT && -t STDIN; # Close out previous pager (if any): if( $interactive ) { select STDOUT; close PAGER; } # Prompt the user for a response: my $response; while( 1 ) { print STDERR $prompt if $interactive; $response = <STDIN>; die "End of input.\n" if ! defined $response; chomp $response; last if ! $interactive || ! $validator || $validator->( $response ); $prompt = $reprompt if $reprompt; } # Run output (until next prompt) through pager: if( $interactive ) { my $pager = $ENV{PAGER} || 'less -EF'; open PAGER, '|-', $pager or die "Can't run $pager: $!\n"; select PAGER; } return $response; }

        The new version above makes the 2nd and 3rd arguments optional.

        Yes, the code:

        last if $validator->( $response );

        means that $validator needed to be a reference to a subroutine and that the subroutine would be passed what the user entered (minus the newline) and should return a false value only if the entered response was not acceptable. Not providing that makes it pointless to provide $reprompt since input would never be declared invalid so reprompting would never happen.

        (Update: For example:

        prompt( "Knarfle the garthog? ", sub { shift(@_) =~ /^[yn]/i }, "Yes or no? ", );

        )

        I should probably test this code, but I haven't. (I probably will later.)

        But I'll repeat that, if you are printing output other than via 'print' (without a file handle argument), then you'll probably want to do the extra work to redirect STDOUT to the pager, rather than just the trivial step of using select like I did.

        The new version also notices if you have either piped in a file or piped the output to a command (like 'less') or a file and doesn't bother writing out prompts or trying to run 'less' in such cases (since such efforts would be mostly pointless).

        Update: I just realized that any output before the first prompt is not piped to an instance of a pager. I'll have to fix that at some point and post an updated version. This seems like exactly the type of thing one could find on CPAN, but I haven't previously run into such (and haven't searched for such yet).

        Update: (late but tiny) Term::DBPrompt provides this type of functionality but rather less conveniently. Just FYI. I didn't find anything else that came close.

        - tye        

Re: Pipping a script with prompts into less
by Corion (Patriarch) on Oct 18, 2012 at 17:08 UTC

    Maybe you could (re)open STDOUT to $ENV{PAGER} once your prompts are done?

Re: Pipping a script with prompts into less
by thargas (Deacon) on Oct 23, 2012 at 14:00 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://999770]
Approved by Corion
Front-paged by Tanktalus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-04-26 00:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found