Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

regex syntax and idomatic Perl

by cbeckley (Curate)
on Mar 21, 2017 at 02:32 UTC ( [id://1185300]=perlquestion: print w/replies, xml ) Need Help??

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

I have input from a "df -k /home" that looks like this:

filesystem kbytes used avail capacity mounted on /dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home

And a function that parses it for the capacity (creatively named no less):

sub parse_capacity { my ($df_output) = @_; my $capacity; if ($df_output =~ m[(\d+)%\s+/home$]) { $capacity = $1; } else { $capacity = 'Match Error'; } return $capacity; }

This works. I should be done right? Noooo, I'm looking for a more idiomatic way to rewrite the "if regex" for purely aesthetic reasons.

I tried:

($df_output =~ m[(\d+)%\s+/home$]) ? $capacity = $1 : $capacity = 'Mat +ch Error';

Which returns 'Match Error' every time.

Why does the ternary statement not work?
What are the some of the more Perlish ways to rephrase that if statment?

Thanks,
cbeckley

Replies are listed 'Best First'.
Re: regex syntax and idomatic Perl
by huck (Prior) on Mar 21, 2017 at 02:43 UTC

      Ah, ok. I see why

      $capacity =($df_output =~ m[(\d+)%\s+/home$]) ? $1 : 'Match Error';

      is correct.

      Thank you for the link, otherwise my next question was going to why the previous version didn't produce an error. However, the fact that this operator can evaluate to an lvalue now prompts the next question, where else does this occur? Does any expression that evaluates to a object or variable work the same way?

      Thanks,
      cbeckley

        ... the fact that this operator can evaluate to an lvalue now prompts the next question, where else does this occur? Does any expression that evaluates to a object or variable work the same way?

        Interesting question. All the more so since I have only a tenuous grasp of the syntactic/semantic issues involved. Obviously, something like

        c:\@Work\Perl\monks>perl -wMstrict -le "my $t = 1; my $f = ''; ;; ($t || $f) = 'foo'; print qq{t '$t' f '$f'}; " Can't modify logical or (||) in scalar assignment ... Execution of -e aborted due to compilation errors.
        doesn't work:  ($t || $f) can't be an lvalue.

        The only other situation I can think of in which this sort of hocus-pocus occurs is with Lvalue subroutines (see perlsub). I'd be interested to learn if there are any others. (Of course, now that I've asked the question, folks will probably respond with a million examples! :)

        BTW: the  ?: ternary operator in C/C++ has the same lvalue selection property.


        Give a man a fish:  <%-{-{-{-<

Re: regex syntax and idomatic Perl
by AnomalousMonk (Archbishop) on Mar 21, 2017 at 08:53 UTC

    The precedence problem has been covered. As for idiomaticity (?), how about:

    c:\@Work\Perl>perl -wMstrict -le "for ('filesystem kbytes used avail capacity mounte +d on', '/dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home' +, ) { printf qq{'%s' -> '%s' \n}, $_, parse_capacity($_); } ;; sub parse_capacity { my ($df_output) = @_; return $df_output =~ m[(\d+)%\s+/home$] ? $1 : 'Match Error'; } " 'filesystem kbytes used avail capacity mounted on' +-> 'Match Error' '/dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home' -> '4 +6'

    Update: Actually, if the structure of a data record really is that simple, it's not that much more effort to go ahead and parse the whole thing:

    c:\@Work\Perl>perl -wMstrict -le "use Data::Dump qw(dd); ;; for ('filesystem kbytes used avail capacity mounte +d on', '/dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home' +, ) { dd $_, parse_all($_); } ;; sub parse_all { my ($df_output) = @_; ;; my $match = my ($fsys, $kb, $used, $avail, $cap, $mtd) = $df_output =~ m{ \A (.+) \s+ (\d+) \s+ (\d+) \s+ (\d+) \s+ (\d+)% \s+ (.+) \z }xms; ;; return { error => 'Match Error' } unless $match; ;; return { fsys => $fsys, kb => $kb, used => $used, avail => $avail, cap => $cap, mounted_on => $mtd, }; } " ( "filesystem kbytes used avail capacity mounted on +", { error => "Match Error" }, ) ( "/dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home", { avail => 6712914, cap => 46, fsys => "/dev/dsk/c0b0t0d0s4", kb => 12209400, mounted_on => "/home", used => 5496486, }, )


    Give a man a fish:  <%-{-{-{-<

      Ah, that's very cool:

      my $match = my ($fsys, $kb, $used, $avail, $cap, $mtd) = $df_output =~ m{\A (.+) \s+ (\d+) \s+ (\d+) \s+ (\d+) \s+ (\d+) % \s+ +(.+) \z}xms;

      I didn't know match could be used in a list context like that. Chaining the scalar assignment is a nice touch.
      Thank you.

      Thanks,
      cbeckley

Re: regex syntax and idomatic Perl
by tybalt89 (Monsignor) on Mar 21, 2017 at 02:46 UTC
    $capacity = $df_output =~ m[(\d+)%\s+/home$] ? $1 : 'Match Error';

    see perlop for precedence problem you had.

Re: regex syntax and idomatic Perl
by NetWallah (Canon) on Mar 21, 2017 at 02:50 UTC
    if (my ($capacity,$loc) = $df_output =~m/(?:\S+\s+){4}(\d+)%\s+(.+)$/) +{ #say "cap=$capacity of $loc"; return $capacity; } return "Match Error";

            ...it is unhealthy to remain near things that are in the process of blowing up.     man page for WARP, by Larry Wall

Re: regex syntax and idomatic Perl
by Monk::Thomas (Friar) on Mar 21, 2017 at 13:49 UTC

    What are the some of the more Perlish ways to rephrase that if statment?

    Here's an alternative solution without a regexp/match condition. It's not intended to be 'more' perlish but 'differently' perlish. TMTOWTDI and all that.

    #!/usr/bin/env perl use strict; use warnings; # in my own code I would use Readonly to define these constants # -- http://search.cpan.org/~sanko/Readonly-2.05/lib/Readonly.pm my $idx_usage = 4; my $idx_mount = 5; my @input = ( '/dev/dsk/c0b0t0d0s4 12209400 5496486 6712914 46% /home', ); printf "cap = %s\n", parse_capacity(@input); exit 0; # ----- sub parse_capacity { # use $_[0] or shift @_ instead of my ($scalar) = @array; my $df_output = $_[0]; my @fields = split /\s+/, $df_output; if ($fields[$idx_mount] eq '/home') { my $use = $fields[$idx_usage]; chop $use; # remove '%' return $use; } else { return 'Match Error'; } }
Re: regex syntax and idomatic Perl
by Anonymous Monk on Mar 21, 2017 at 06:43 UTC

    Hi

    Should $capacity = $1 be $capacity == $1?

    J.C.

      if ($df_output =~ m[(\d+)%\s+/home$]) { $capacity = $1; }
      No. It's really an assignment, not a comparison.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-16 10:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found