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

What I want to do is as follows:

system("$cmd|more"); =anything do A if terminated by press q do B otherwise. =cut

That said, $cmd will output response piped to more,then I need to do different things whether it's terminated by press q.

But how to know whether user terminates system("$cmd|more") with q or not in the first place?

Replies are listed 'Best First'.
Re: How to know whether user terminates system("$cmd|more") with q or not?
by BrowserUk (Patriarch) on Sep 13, 2011 at 11:59 UTC
    But how to know whether user terminates system("$cmd|more") with q or not in the first place?

    There is no (simple*) way to do this. The 'q' character is only seen by the more process and it doesn't share any information about how it is terminated.

    The simplest way to achieve something similar would be to use a piped-open to run the command (without using more) and Term::ReadKey to simulate the more commands.

    (*) Theoretically it would be possible to have the parent process capture all input from the console and inject it into the child processes input stream, but it would be a substantial amount of very non-trivial coding. Using a piped-open and simulating more is much easier.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: How to know whether user terminates system("$cmd|more") with q or not?
by zentara (Cardinal) on Sep 13, 2011 at 11:57 UTC
    But how to know whether user terminates system("$cmd|more") with q or not in the first place?

    It probably will get more complicated than you want. You want to intercept the q keypress.

    Here is a simple example, (or you can move up to an eventloop system, or watch STDIN in the separate thread, and let it watch for q). I 'm not sure how to pass the q to killing your command , unless you use a piped open or some other IPC module that returns a pid. Maybe someone knows a clever trick for that using return values from system.

    #!/usr/bin/perl use warnings; use strict; use Term::ReadKey; #passing ReadKey() an argument of -1 to indicate not to block: ReadMode('cbreak'); while(1){ my $char; if (defined ($char = ReadKey(0)) ) { print "$char->", ord($char),"\n"; # input was waiting and i +t was $char if(ord($char) == 113){ print "Got a q\n"; print "Do your thing + here\n" } if(ord($char) == 27){ print "Got an Escape\n"; exit; } } else { # no input was waiting } } ReadMode('normal'); # restore normal tty settings

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

      You mentioned

      watch STDIN in the separate thread, and let it watch for q

      Will it work theoretically?

      Say, will the thread be able to capture q if it's already consumed by more?

        Will it work theoretically?Say, will the thread be able to capture q if it's already consumed by more?

        Theoretically yes,I think your only way to do it that way is to somehow dup stdin. See

        Found in /usr/lib/perl5/5.14.1/pod/perlfaq5.pod How do I dup() a filehandle in Perl? If you check "open" in perlfunc, you'll see that several of the way +s to call open() should do the trick. For example: open my $log, '>>', '/foo/logfile'; open STDERR, '>&LOG'; Or even with a literal numeric descriptor: my $fd = $ENV{MHCONTEXTFD}; open $mhcontext, "<&=$fd"; # like fdopen(3S) Note that "<&STDIN" makes a copy, but "<&=STDIN" makes an alias +. That means if you close an aliased handle, all aliases become inacce +ssible. This is not true with a copied one. Error checking, as always, has been left as an exercise for the + reader.
        Then your stdin watcher thread and main thread might be able to both get the q . As with error checking, seeing if it works is left as an exercise to the OP. :-). Googling for "perl duping stdin" should give you some already written code.

        But I think you are trying to do this the hard way, you probably would be better off putting your code into an eventloop of some sort, and using a piped open to run your $cmd.


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku ................... flash japh
Re: How to know whether user terminates system("$cmd|more") with q or not?
by salva (Canon) on Sep 13, 2011 at 12:42 UTC
    system "strace -o /tmp/strace.out -e open,read more @ARGV"; open my $trace, "/tmp/strace.out" or die; my $last = ''; while (<$trace>) { if (/read\(2,\s*"(.*)",\s*1\)\s*=\s*1$/) { $last = $1; } } print "exited with key >$last<\n";
    But it is so weak...

    update: How about Ctrl-C?

    system "strace -o /tmp/strace.out -e open,read more @ARGV"; open my $trace, "/tmp/strace.out" or die; my $last = ''; while (<$trace>) { if (/read\(2,\s*"(.*)",\s*1\)\s*=\s*1$/ or /--- SIG(\w+)/) { $last = $1; } } print "exited with key >$last<\n";
Re: How to know whether user terminates system("$cmd|more") with q or not?
by hbm (Hermit) on Sep 13, 2011 at 15:57 UTC

    I'm trying to imagine why you care whether the user views all the output...

    But anyhow, what if the user enters Q or Ctrl-C or Ctrl-Z or ! pkill more?

    Update:

    $ man more EXIT STATUS The following exit values are returned: 0 Successful completion. >0 An error occurred. --More--(90%)

    And (at least) the following are considered successful completion:

    • q, Q, or <backspace>
    • paging through everything
    • when there is no need for paging, like more /dev/null
Re: How to know whether user terminates system("$cmd|more") with q or not?
by DrHyde (Prior) on Sep 14, 2011 at 09:29 UTC

    Simply check the return value from system().

    $ perl -e 'print system("some thing|more")."\n"'
    prints 0 on this 'ere system if I quit 'more' by hitting q, 2 if I quit by hitting control-C, 35072 if I kill-9 the 'more' process, and 36608 if I kill -15. The exact values returned may differ slightly from one implementation of 'more' to another, but you're probably safe if you assume that 0 means the user hit q and non-zero means that 'more' terminated for some other reason - just don't try to portably determine what that other reason was!
      0 means the user hit q and non-zero means that 'more' terminated for some other reason

      What value do you get if the user allows more to terminate on eof?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.