简体   繁体   中英

Perl - Searching values in a log file and store/print them as a string.

I would like to search values after a specific word (Current Value = ) in a log file, and makes a string with values.

vcs_output.log: a log file

** Fault injection **
  Count = 1533
  0: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[6]
  0: Current value = x
  1: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[51]
  1: Current value = x
  2: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[3]
  2: Current value = 1
  3: Path = cmp_top.iop.sparc0.exu.alu.shft_alu_shift_out_e[18]
  3: Current value = 0
  4: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs3_data_e[17]
  4: Current value = x
  5: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs1_data_e[43]
  5: Current value = 0
  6: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rcc_data_e[38]
  6: Current value = x
  7: Path = cmp_top.iop.sparc0.exu.alu.byp_alu_rs2_data_e_l[30]
  7: Current value = 1
   .
   .
   .

If I store values after "Current value = ", then x,x,1,0,x,0,x,1. I ultimately save/print them as a string such as xx10x0x1.

Here is my code code.pl:

#!/usr/bin/perl 

use strict;
use warnings;
##### Read input
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!; 
chomp ( my @input = <$input_fh> );

my $i=0;
my @arr;
while (@input) {
    if (/Current value = /)
    $arr[i]= $input;  # put the matched value to array 
   }

}

## make a string from the array using an additional loop 

close ( $input_fh );

I think there is a way to make a string in one loop (or even not using a loop). Please advise me to make it. Any suggestion is appreciated.

You can use capturing parentheses to grab the string you want:

use strict;
use warnings;

my @arr;
open ( my $input_fh, '<', 'vcs_output.log' ) or die $!; 
while (<$input_fh>) {
    if (/Current value = (.)/) {
        push @arr, $1;
    }
}
close ( $input_fh );
print "@arr\n";

__END__

x x 1 0 x 0 x 1

You can do both that you ask for.

To build a string directly, just append to it what you capture in the regex

my $string;
while (<$input_fh>) 
{
    my ($val) = /Current\s*value\s*=\s*(.*)/;
    $string .= $val;
}

If the match fails then $val is an empty string, so we don't have to test. You can also write the whole while loop in one line

$string .= (/Current\s*value\s*=\s*(.*)/)[0] while <$input_fh>;

but I don't see why that would be necessary. Note that this reads from the filehandle, and line by line. There is no reason to first read all lines into an array.


To avoid (explicit) looping, you can read all lines and pass them through map , naively as

my $string = join '', 
    map { (/Current\s*value\s*=\s*(.*)/) ? $1 : () } <$input_fh>;

Since map needs a list, the filehandle is in list context , returning the list of all lines in the file. Then each is processed by code in map 's block, and its output list is then joined.

The trick map { ($test) ? $val : () } map { ($test) ? $val : () } uses map to also do grep 's job, to filter -- the empty list that is returned if $test fails is flattened into the output list, thus disappearing. The "test" here is the regex match, which in the scalar context returns true/false, while the capture sets $1 .

But, like above, we can return the first element of the list that match returns, instead of testing whether the match was successful. And since we are in map we can in fact return the "whole" list

my $string = join '', 
    map { /Current\s*value\s*=\s*(.*)/ } <$input_fh>;

what may be clearer here.


Comments on the code in the question

  • the while (@input) is an infinite loop, since @input never gets depleted. You'd need foreach (@input) -- but better just read the filehandle, while (<$input_fh>)

  • your regex does match on a line with that string, but it doesn't attempt to match the pattern that you need (what follows = ). Once you add that, it need be captured as well, by ()

  • you can assign to the i-th element (which should be $i ) but then you'd have to increment $i as you go. Most of the time it is better to just push @array, $value

Use grep and perlre
http://perldoc.perl.org/functions/grep.html
http://perldoc.perl.org/perlre.html

If on a non-Unix environment then...

#!/usr/bin/perl -w
use strict;

open (my $fh, '<', "vcs_output.log");

chomp (my @lines = <$fh>);

# Filter for lines which contain string 'Current value'
@lines = grep{/Current value/} @lines;

# Substitute out what we don't want... leaving us with the 'xx10x0x1'
@lines = map { $_ =~ s/.*Current value = //;$_} @lines;
my $str = join('', @lines);

print $str;

Otherwise...

#!/usr/bin/perl -w
use strict;

my $output = `grep "Current value" vcs_output.log | sed 's/.*Current value = //'`;

$output =~ s/\n//g;
print $output;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM