Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

Re: Pipping a script with prompts into less (edit script)

by tye (Sage)
on Oct 18, 2012 at 17:14 UTC ( #999775=note: print w/replies, xml ) Need Help??

in reply to Pipping a script with prompts into less

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        

Replies are listed 'Best First'.
Re^2: Pipping a script with prompts into less (edit script)
by xorl (Deacon) on Oct 18, 2012 at 17:40 UTC

    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

    ./ | less
    all I see at first is:
    After I type my answer (which I can't see), I'm in less with just
    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        

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://999775]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2018-05-26 14:26 GMT
Find Nodes?
    Voting Booth?