perl - remove new line after comma within function definitions only - regex

Regex command to remove new line after comma within function definitions only.
ChainCtrlUpdateHandler defaultUpdateHandlers[kVideoRouteNodeMax] =
{
ChainCtrlUpdateMonitorRoute,
ChainCtrlUpdateVideoOutRoute,
};
eErrorT ChainCtrlInitChains(ChainCtrlT* pChainCtrl,
char* name,
int instance,
void* pOwner,
)
{
...
}
OUTPUT DESIRED
ChainCtrlUpdateHandler defaultUpdateHandlers[kVideoRouteNodeMax] =
{
ChainCtrlUpdateMonitorRoute,
ChainCtrlUpdateVideoOutRoute,
};
eErrorT ChainCtrlInitChains(ChainCtrlT* pChainCtrl,char* name,int instance,void* pOwner)
{
....
}
NOTE
There are many function definitions in the .c file
MY CODE
open(my $FILE, "< chaincontroller.c") or die $!;
my #arr = <$FILE>;
foreach (#arr){
$_ =~ s/,\n/,/;
print $_;
}
It removes ',\n' everywhere but i need it to be done only for functions definitions only.

Try doing this :
perl -00 -ple 's/,\s*\n/,/gms if /\(/ .. /\)/' filename.txt
With a script :
perl -MO=Deparse -00 -ple 's/,\s*\n/,/gms if /\(/ .. /\)/' filename.txt
code :
BEGIN { $/ = ""; $\ = "\n\n"; }
LINE: while (defined($_ = <ARGV>)) {
chomp $_;
s/,\s*\n/,/gms if /\(/ .. /\)/;
}
continue {
die "-p destination: $!\n" unless print $_;
}

Related

Check if multiple lines exist in text file

Using Perl I would like to check if the two lines highlighted below exist in a text file . Each line is preceded by a tab.
CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES;
CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES;
***CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES;***
CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES;
CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES;
***CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES;***
CF=CFD-TS10-REG-9124445544-YES-YES;
I am using the following if statement but it is not matched
if (/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/ && /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/)
{
say "this case is found here .....";
}
What am I doing wrong ?
Edited
This is the program I wrote :-
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my $HSSIN='D:\testproject\HSS-export-test-run-small.txt';
my $ofile = 'D:\testproject\HSS-output.txt';
open (INFILE, $HSSIN) or die "Can't open input file";
open (OUTFILE,"> $ofile" ) or die "Cant open file";
my $add;
my $MSISDN;
my $line;
sub callForwardingsCF()
{
if (/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/ && /\t*CF=(CFB-TS10-ACT-(NONE|+\d+))/)
{
say "this case is found here .....";
}
} # end sub callForwardingsCFD
while (<INFILE>)
{
if (/<SUBEND/)
{
say "SUBEND found";
#$line = $1 if /^\s*MSISDN=(\d+);/;
print OUTFILE "processSingle UpdateCommand GSUB MKEY $line";
print OUTFILE "\n";
}
if ($_ =~ /^\t*MSISDN=(\d+);/)
{ #find MSISDN in file global search
say "STARTER MSISDN is $1";
$MSISDN = $1;
$add = $1;
$line = "$1"; #group 1
}
callForwardingsCF(); #callForwardings
}
close INFILE;
close OUTFILE;
Example of a record in the input file
<BEGINFILE>
<SUBBEGIN
IMSI=232191400029053;
MSISDN=4369050064401;
DEFCALL=TS11;
CURRENTNAM=BOTH;
CAT=COMMON;
TBS=TS11&TS12&TS21&TS22;
VLRLIST=10;
SGSNLIST=10;
SMDP=MSC;
CB=BAOC-ALL-PROV;
CB=BOIC-ALL-PROV;
CB=BOICEXHC-ALL-PROV;
CB=BICROAM-ALL-PROV;
CW=CW-ALL-PROV;
CF=CFU-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFB-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-65535-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFNRY-ALL-PROV-NONE-YES-YES-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFNRC-ALL-PROV-NONE-YES-NO-NONE-YES-65535-NO-NO-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-65535-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;
CF=CFD-TS10-REG-91436903000-YES-YES-25-YES-65535-YES-YES-NO-NO-NO-YES-YES-YES-YES-NO;
TCSISTATE=YES;
OCSISTATE=YES;
CONTROL=SUB;
WPA=0;
GS=HOLD&MPTY&ECT&CLIR&CLIP;
CLIRES=TEMPALLOW;
CLIPOC=NO;
OCSI=10;
CFSMS=ACT-10-914366488325207-YES-YES-NO-NO-NO;
ARD=PROV;
SUBRES=ALLPLMN;
IST_ALERT_TIMER=120;
IST_ALERT_RESPONSE=2;
SUB_AGE=0;
MIMSI=240076400029053-ONELIVE-2-2-1-0-0;
MIMSI=232191400029053-ONELIVE-1-1-1-0-0;
SID=2805158185721065;
MCSISTATE=YES;
CLRBSG=CLIP-YES-NO-NO-NO-NO;
UPLCSLCK=NO;
UPLPSLCK=NO;
DEFOFAID=10;
EPS_PROFILE_ID=1;
TGPPAMBRMAXUL=50000000;
TGPPAMBRMAXDL=150000000;
ARD_EXT=NULL-NULL-NULL-N3GPPNOTALLOWED;
FRAUDTPL_ID=10;
HLR_INDEX=1;
LTEAUTOPROV=NO;
PSSER=1-1-10-1-NONE-DYNAMIC-00000000;
EPSSER=1-10-10-1-NONE-DYNAMIC-00000000-1;
MPS=NO;
<SUBEND
Thanks,
Graham
Per default regexes match linewise.
So if you were trying to match an input that contains multiple lines, you would have to use one of the modifiers that allows the regex to match the entire string.
See the the perl regex documentation - the chapter "Modifiers".
Then you should add the s modifiler and change your if statement to:
if ( /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/s &&
/\t*CF=(CFU-TS10-ACT-(NONE|\d+))/s ) {
say "found";
}
If you read linewise you will never have both of your regexes match for the same line, so you would need to do your regexes seperately as already suggested by the other answer.
#$/ = ""; #without paragraph mode
open my $file, '<', 'data_file';
binmode $file;
while(<$file>){
print $_ if ( $_ =~ /\s+CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;/ ||
$_ =~ /\s+CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;/ );
}
EDIT:
OR, you can do it in paragraph mode if conditions allow it.
$/ = "";
open my $file, '<', 'data_file';
binmode $file;
while(<$file>){
(undef, $first) = split (/\s+(CF=CFU-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;)/, $_);
(undef, $second) = split(/\s+(CF=CFB-TS10-ACT-NONE-YES-NO-NONE-YES-\d+-YES-YES-NO-NO-NO-NO-NO-NO-NO-NO;)/, $_ );
print $first . "\n" . $second;
}
Code is tested and seems to work fine with supplied data.
Also, those are not tabs "\t" ... those are spaces "\s+" preceding those lines. Best thing is to learn your data set before you try to parse it ;)
Typically perl processes file "line by line".
Try something like sample script below:
my($line1,$line2);
while(<STDIN>) {
$line1=$_ if /\t*CF=(CFU-TS10-ACT-(NONE|\d+))/
$line2=$_ if /\t*CF=(CFB-TS10-ACT-(NONE|\d+))/
if( $line1 and $line2 ) {
say "this case is found here .....";
last; # skip processing remaning lines
}
}
Alternatively you may "slurp" whole file into one scalar variable.

Use of uninitialized value in pattern match

I have the following code in a subroutine in Perl for which I keep getting the following error :
Use of uninitialized value $nextLine in pattern match (m//) at catlist.pl line 67, line 2756.
sub extract_testdesc {
my #str = #_;
my $file = $_[0];
my $testname = $_[1];
my #fifo;
# Open the file
open( FILEHANDLE, $file ) or die "couldnt open";
while (<FILEHANDLE>) {
if ( $_ =~ m/\/\*\*/ ) { # if start of comment /**
undef(#fifo);
$nextLine = <FILEHANDLE>;
while ( $nextLine !~ m/\*\// ) { # Add all lines into array until */ is encountered
if ( $nextLine !~ m/\#testlogic.author/ ) {
$nextLine =~ s/\*//g;
if ( $nextLine ne "" ) {
push( #fifo, $nextLine );
}
}
$nextLine = <FILEHANDLE>;
}
}
if ( $_ =~ m/$testname/ ) {
return (#fifo);
}
}
close(FILEHANDLE);
}
What am I doing wrong ? I'm new to Perl so any help is appreciated.
Whenever you use a while loop on a file handle, it's actually synonymous with while (defined($_ = <FILEHANDLE>)) {. This is useful because once the filehandle reaches the eof, it will exit the loop. On the other hand, you are doing manual calls readline calls without testing to see if anything was returned, hence your uninitialized value warnings.
Overall, your goal and logic are confusing. However, perhaps an introduction to the range operator will help you? The following achieves what I think you logic is, but I easily could have misinterpreted.
sub extract_testdesc {
my ($file, $testname) = #_;
my #fifo;
# Open the file
open my $fh, '<', $file or die "couldnt open: $!";
while (<$fh>) {
if ( my $range = m{\Q/**} .. m{\Q*/}) {
#fifo = () if $range == 1;
push #fifo, $_;
} elsif ($_ =~ m/\Q$testname\E/ ) {
return (#fifo);
}
}
close($fh);
}

How to match a regex pattern for multiple files under a directory in perl?

I wrote a script to match a pattern and return a statement for a file
#!/usr/bin/perl
use strict;
use warnings;
my $file = '/home/Sidtest/sid.txt';
open my $info , $file or die " Couldn't open the $file:$!";
while( my $line = <$info>) {
if ($line =~ m/^#LoadModule ssl_module/) {
print "FileName =",$file," Status = Failed \n";
}
elsif ($line =~ m/^LoadModule ssl_module/) {
print "FileName =",$file," Status = Passed \n";
}
}
close $info;
So now I am trying to modify this script to work for multiple files under the same directory. I haven't been able to do that successfully. Can anyone please help in how I can make it work for any number of files in a directory.
This will read every file in ./directory and foreach file, print out each line.
The print statement can be altered to print if /match/, or whatever you want:
my #dir = <directory/*>;
foreach my $file (#dir){
open my $input, '<', $file;
while (<$input>){
print "PASS: $_\n" if m/^#LoadModule ssl_module/;
[...]
}
}
The variable #ARGV contains a list of arguments sent to the script when started. Loop through #ARGV and call the script with the files you want to process:
#!/usr/bin/perl
use strict;
use warnings;
foreach my $file (#ARGV) {
open my $info , $file or die " Couldn't open the $file:$!";
while( my $line = <$info>) {
if ($line =~ m/^#LoadModule ssl_module/) {
print "FileName =",$file," Status = Failed \n";
}
elsif ($line =~ m/^LoadModule ssl_module/) {
print "FileName =",$file," Status = Passed \n";
}
}
close $info;
}
# process all files *.txt in your dir: ./myscript.pl /home/Sidtest/*.txt
Check perldoc perlrun, and look at the -p and -n parameters. Essentially, they treat your script as if it were the contents of a loop over stdin, where stdin is generated by iterating through the files supplied on the command line. The name of the file currently-being-processed can be accessed using the $ARGV variable.
So, you might go for an approach where your whole script looks more like this, using the -n param, where $_ contains the current line.:
if ( m/^#LoadModule ssl_module/) {
print "FileName =",$ARGV" Status = Failed \n";
} elsif (m/^LoadModule ssl_module/) {
print "FileName =",$ARGV," Status = Passed \n";
}

Finding the last "}" of a subroutine

Supposed I have a file with Perl-code: does somebody know, if there is a module which could find the closing "}" of a certain subroutine in that file.
For example:
#!/usr/bin/env perl
use warnings;
use 5.012;
routine_one( '{°^°}' );
routine_two();
sub routine_one {
my $arg = shift;
if ( $arg =~ /}\z/ ) {
say "Hello my }";
}
}
sub routine_two {
say '...' for 0 .. 10
}
The module should be able to remove the whole routine_one or it should can tell me the line-number of the closing "}" from that routine.
You want to use PPI if you are going to be parsing Perl code.
#!/usr/bin/env perl
use warnings;
use 5.012;
use PPI;
my $file = 'Example.pm';
my $doc = PPI::Document->new( $file );
$doc->prune( 'PPI::Token::Pod' );
$doc->prune( 'PPI::Token::Comment' );
my $subs = $doc->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
die if #$subs != 1;
my $new = PPI::Document->new( \qq(sub layout {\n say "my new layout_code";\n}) );
my $subs_new = $new->find( sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name eq 'layout' } );
$subs->[0]->block->insert_before( $subs_new->[0]->block ) or die $!;
$subs->[0]->block->remove or die $!;
# $subs->[0]->replace( $subs_new->[0] );
# The ->replace method has not yet been implemented at /usr/local/lib/perl5/site_perl/5.12.2/PPI/Element.pm line 743.
$doc->save( $file ) or die $!;
The following will work in case your subroutines don't contain any blank lines, like the one in your example:
#!/usr/bin/perl -w
use strict;
$^I = ".bkp"; # to create a backup file
{
local $/ = ""; # one paragraph constitutes one record
while (<>) {
unless (/^sub routine_one \{.+\}\s+$/s) { # 's' => '.' will also match "\n"
print;
}
}
}

What is the actual Perl code that will mimic -pi inside the program?

This seems redundant, running perl from a Perl script itself.
my $Pref = "&*())(*&^%$##!";
system("perl -pi -e 's|^SERVERNAME.*\$|SERVERNAME \"\Q$Pref\E\"|g' pserver.prefs");
What is the actual Perl code that will mimic -pi? I just want something that would work like sed on Perl, just as simple as can be.
Based on Todd Gardner's site, it seems it basically just reads and writes all of the file, attempting to apply the regex to every line. The solution was a bit complex for a noob Perl user like me, so I dumbed it down using:
my $ftp = "/home/shared/ftp";
my $backup = $ftp . ".bak";
rename($ftp, $backup);
open (FTP, "<", $backup) or die "Can't open $backup: $!";
open (FTP_OUT, ">", $ftp) or die "Can't open $ftp: $!";
while (<FTP>)
{
$_ =~ s|$panel_user \Q$panel_oldpass\E |$panel_user \Q$panel_newpass\E |g;
print FTP_OUT $_;
}
close(FTP);
close(FTP_OUT);
Is there anything wrong with using two opens? Should this be avoided or is it ok for a simple solution?
I must admit, a system sed command is much more simple and cleaner.
Check out perlrun which describes the options in detail. In particular, it lays out some options:
From the shell, saying
$ perl -p -i.orig -e "s/foo/bar/; ... "
is the same as using the program:
#!/usr/bin/perl -pi.orig
s/foo/bar/;
which is equivalent to
#!/usr/bin/perl
$extension = '.orig';
LINE: while (<>) {
if ($ARGV ne $oldargv) {
if ($extension !~ /\*/) {
$backup = $ARGV . $extension;
}
else {
($backup = $extension) =~ s/\*/$ARGV/g;
}
rename($ARGV, $backup);
open(ARGVOUT, ">$ARGV");
select(ARGVOUT);
$oldargv = $ARGV;
}
s/foo/bar/;
}
continue {
print; # this prints to original filename
}
select(STDOUT);
I'd just use Tie::File.
use Tie::File;
use File::Copy;
copy $file, "$file.bak" or die "Failed to copy $file to $file.bak: $!";
tie #array, "Tie::File", $file or die "Can't open $file: $!";
s/foo/bar/ for #array;
Perlmonks has a suggestion equivalent to:
use English qw<$INPLACE_EDIT>;
{
local ($INPLACE_EDIT, #ARGV) = ('.bak', #files);
while (<>) {
s/this/that/;
print;
}
}
Also recommended in the same thread is Sysadm::Install::pie
B::Deparse is your friend:
cowens#amans:~/test$ perl -MO=Deparse -pi -e 1
BEGIN { $^I = ""; }
LINE: while (defined($_ = <ARGV>)) {
'???';
}
continue {
print $_;
}
-e syntax OK
From this we can see that $^I is what implements in-place editing. Just set #ARGV to the files you want to edit, and loop away.