How to grep square brackets in perl - perl

I am trying to grep [0](including square brackets) in a file using perl, I tried following code
my #output = `grep \"\[0\]\" log `;
But instead of returning [0], it is giving output where it matches 0

Your problem is that you need to escape the [ and ] twice, as [ ... ] has a special meaning in regexes (it defines a character class).
#!/usr/bin/perl
use strict;
use warnings;
my #output = `grep "\\[0\\]" log `;
print for #output;
But you really don't need to use the external grep command. Perl is great at text processing.
#!/usr/bin/perl
use strict;
use warnings;
while (<>) {
print if /\[0\]/;
}
My solution reads from any file whose name is given as an argument to the program (or from STDIN).

Related

How I search and print matched wold in UNIX or perl?

1=ABC,2=mnz,3=xyz
1=pqr,3=ijk,2=lmn
I have this in text file I want to search 1= and that should print only matched word 1=ABC and 1=pqr
Any suggestions in Perl or Unix?
Input:
$ cat grep.in
1=ABC,2=mnz,3=xyz
1=pqr,3=ijk,2=lmn
4=pqr,3=ijk,2=lmn
Command:
$ grep -o '1=[^,]\+' grep.in
1=ABC
1=pqr
Explanations:
You can just use grep on your input
-o is to output only the matching pattern
1=[^,]\+ the regex will match strings that start by 1= followed by at least one character that is not a comma (I have based this on the hypothesis that there is no comma in the right part of the = except the separator)
if you want to accept empty result you can change the \+ by *
It appears that your input data is in CSV format. Here is a Perl solution based on Text::CSV
parse the CSV content row-wise
print out columns that start with 1=
#!/usr/bin/perl
use warnings;
use strict;
use Text::CSV;
my $csv = Text::CSV->new({
binary => 1,
eol => "\n",
}) or die "CSV\n";
# parse
while (my $row = $csv->getline(\*DATA)) {
foreach (#{ $row }) {
print "$_\n" if /^1=/;
}
}
exit 0;
__DATA__
1=ABC,2=mnz,3=xyz
1=pqr,3=ijk,2=lmn
Test run:
$ perl dummy.pl
1=ABC
1=pqr
Replace DATA with STDIN to read the input from standard input instead.

How to read the textfile by command line arguments and print the column by using perl?

How to read the text file using perl command line arguments and print the third column using perl?
I'm struck with taking input from the command line and printing the required column. Help me to choose the right way to reach the expected output.
Code which I wrote to take command line input:(map.pl)
use strict;
use warnings 'all';
use Getopt::Long 'GetOptions';
my #files=GetOptions(
'p_file=s' => \my $p_file,
);
print $p_file ? "p_file = $p_file\n" : "p_file\n";
Output I got for above code:
perl map.pl -p_file cat.txt
p_file = cat.txt
cat.txt:(Input file)
ADG:YUF:TGH
UIY:POG:YTH
GHJUR:"HJKL:GHKIO
Expected output:
TGH
YTH
GHKIO
Perl can automatically read files whose names are provided as command line arguments. The command below should produce your expected output
perl -F: -le 'print $F[2]' cat.txt
-F: turns on autosplit mode, sets the field separator to : and loops over lines of input files. -l handles line endings during input and output. The code after e flag ('print $F[2]' prints 3rd field) is executed for each line of file. Find out more by reading perldoc perlrun.
You'd need to read the file and split the lines to get the columns, and print the required column. Here's a demo code snippet, using the perl -s switch to parse command line arguments. Run like this ./map.pl -p_file=cat.txt
#!/usr/bin/perl -s
use strict;
use warnings;
use vars qw[$p_file];
die("You need to pass a filename as argument") unless(defined($p_file));
die("Filename ($p_file) does not exists") unless(-f $p_file);
print "Proceeding to read file : $p_file\n\n";
open(my $fh,'<',$p_file) or die($!);
while(chomp(my $line = <$fh>)) {
next unless(defined($line) && $line);
my #cols = split(/:/,$line);
print $cols[-1],"\n";
}
close($fh);

replace small numbers in Perl

In a text file, I have many lines looking like (a,b,c) where a, b, and c are double precision real numbers, for instance (8.27605704077856,0.505526531790625,1.15577754382534e-05). Is there a simple way to replace numbers smaller than 10e-4 by 0 in Perl?
Edit: For instance, the text file to be treated looks like:
\plotinstruction[color,style,width]
points{
(8.27,0.5,1.1e-05)
(8.26,1,4.1e-06)
(8.25,1.5,3e-06)
}
and I want to write in a new file:
\plotinstruction[color,style,width]
points{
(8.27,0.5,0)
(8.26,1,0)
(8.25,1.5,0)
}
Perhaps I'm missing something, but perhaps use of map would help?
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
my #values = (8.27605704077856, 0.505526531790625, 1.15577754382534e-05);
my #filtered_values = map(($_ > 1e-4) ? $_ : 0, #values);
print Dumper \#filtered_values;
Results:
$VAR1 = [
'8.27605704077856',
'0.505526531790625',
0
];
To parse input, you could use a regular expression to extract a comma-separated string of numbers, using split on that to get a Perl list to run map upon.
You can write:
perl -pwe 's/\d[\d.e+-]+/$& < 0.001 && $& > -0.001 ? "0" : $&/ge' < INPUT > OUTPUT
(-p means to read in the input, one line at a time, into $_, run the program, print out $_, and loop again; -w enables warnings; -e means that the program is specified directly as a command-line argument; s/// is a regex-substitution; /g means that it's a "global" substitution; and /e means that the replacement-text should be treated as a full Perl expression, rather than as a string with mere variable interpolation.)

How to perform a series of string replacements and be able to easily undo them?

I have a series of strings and their replacements separated by spaces:
a123 b312
c345 d453
I'd like to replace those strings in the left column with those in the right column, and undo the replacements later on. For the first part I could construct a sed command s/.../...;s/.../... but that doesn't consider reversing, and it requires me to significantly alter the input, which takes time. Is there a convenient way to do this?
Listed some example programs, could be anything free for win/lin.
Text editors provide "undo" functionality, but command-line utilities don't. You can write a script to do the replacement, then reverse the replacements file to do the same thing in reverse.
Here's a script that takes a series of replacements in 'replacements.txt' and runs them against the script's input:
#!/usr/bin/perl -w
use strict;
open REPL, "<replacements.txt";
my #replacements;
while (<REPL>) {
chomp;
push #replacements, [ split ];
}
close REPL;
while (<>) {
for my $r (#replacements) { s/$r->[0]/$r->[1]/g }
print;
}
If you save this file as 'repl.pl', and you save your file above as 'replacements.txt', you can use it like this:
perl repl.pl input.txt >output.txt
To convert your replacements file into a 'reverse-replacements.txt' file, you can use a simple awk command:
awk '{ print $2, $1 }' replacements.txt >reverse-replacements.txt
Then just modify the Perl script to use the reverse replacements file instead of the forward one.
use strict;
use warnings;
unless (#ARGV == 3) {
print "Usage: script.pl <reverse_changes?> <rfile> <input>\n";
exit;
}
my $reverse_changes = shift;
my $rfile = shift;
open my $fh, "<", $rfile or die $!;
my %reps = map split, <$fh>;
if ($reverse_changes) {
%reps = reverse %reps;
}
my $rx = join "|", keys %reps;
while (<>) {
s/\b($rx)\b/$reps{$1}/g;
print;
}
The word boundary checks \b surrounding the replacements will prevent partial matches, e.g. replacing a12345 with b31245. In the $rx you may wish to escape meta characters, if such can be present in your replacements.
Usage:
To perform the replacements:
script.pl 0 replace.txt input.txt > output.txt
To reverse changes:
script.pl 1 replace.txt output.txt > output2.txt

Read from a file and compare the content with a variable

#!/usr/bin/perl
some code........
..................
system ("rpm -q iptables > /tmp/checkIptables");
my $iptables = open FH, "/tmp/checkIptables";
The above code checks whether iptables is installed in your Linux machine? If it is installed the command rpm -q iptables will give the output as shown below:
iptables-1.4.7-3.el6.x86_64
Now I have redirected this output to the file named as checkIptables.
Now I want to check whether the variable $iptables matches with the output given above or not. I do not care about version numbers.
It should be something like
if ($iptables eq iptables*){
...............
.......................}
But iptables* gives error.
You could use a regex to check the string:
$iptables =~ /^iptables/
Also, you do not need a tmp file, you can instead open a pipe:
use strict;
use warnings;
use autodie;
open my $fh, '-|', "rpm -q iptables";
my $line = <$fh>;
if ($line =~ /^iptables/) {
print "iptables is installed";
}
This will read the first line of the output, and check it against the regex.
Or you can use backticks:
my $lines = `rpm -q iptables`;
if ($lines =~ /^iptables/) {
print "iptables is installed";
}
Note that backticks may return more than one line of data, so you may need to compensate for that.
I think what you're looking for is a regular expression or a "pattern match". You want the string to match a pattern, not a particular thing.
if ( $iptables =~ /^iptables\b/ ) {
...
}
=~ is the binding operator and tells the supplied regular expression that its source is that variable. The regular expression simply says look at the beginning of the string for the sequence "iptables" followed by a "word-break". Since '-' is a "non-word" character (not alphanumeric or '_') it breaks the word. You could use '-' as well:
/^iptables-/
But you can probably do the whole thing with this statement:
use strict;
use warnings;
use List::MoreUtils qw<any>;
...
if ( any { m/^iptables-/ } `rpm -q iptables` ) {
...
}
piping the output directly into a list via backticks and searching through that list via any (See List::MoreUtils::any
Why not just look at the return value of "rpm -q", which will return 0 or 1 whether it is installed or not respectively?