Im trying to code a "service" script based on "ps".
my code:
#!/usr/bin/perl
use strict;
use warnings;
die "usage: $0 <service name>\n" unless $ARGV[0];
my $service = $ARGV[0];
open(my $ps, "ps -aux |") || die "Uknown command\n";
my #A = <$ps>;
close $ps;
foreach my $i(grep /$service/, #A){
chomp $i;
if($i=~ /root/){
next
}
print "$i\n";
}
My problem: When running the script against undef arg like:
$0 blablabla
I want to return an output if there is no such service appears/when returns 0
Thanks
I assume what you are asking is: How to give a proper message when no matching lines are found?
Well, just store the result in an array instead:
my #lines = grep { !/root/ && /$service/ } #A;
if (#lines) { # if any lines are found
for my $line (#lines) {
...
}
} else {
print "No match for '$service'!\n";
}
Or you can print the number of matches regardless of their number:
my $found = #lines;
print "Matched found: $found\n";
Note also that you can add the check for root in your grep.
As a side note, this part:
die "usage: $0 <service name>\n" unless $ARGV[0];
my $service = $ARGV[0];
Is perhaps better written
my $service = shift;
die "usage ...." unless defined $service;
Which specifically checks if the argument is defined or not, as opposed to true or not.
If I understand you correctly, you want to inform the user if no such service was found? If so, you can modify the script as follows:
my $printed; # Will be used as a flag.
foreach my $i(grep /$service/, #A){
chomp $i;
if($i=~ /root/){
next
}
$printed = print "$i\n"; # Set the flag if the service was found.
}
warn "No service found\n" unless $printed;
You can try something like this:
my #processes = grep /$service/, #A;
if ( scalar #processes ) {
foreach my $i( #processes ){
chomp $i;
if($i=~ /root/){
next;
}
print "$i\n";
}
}
else {
print 'your message';
}
You could check the result of the grep command before traversing it in the for loop, like:
...
my #services = grep { m/$service/ } #A;
# Filter the perl process running this script and...
if ( ! #services ) {
print "No service found\n";
exit 0;
}
foreach my $i( #services ){
...
}
Take into account that the grep command will never give a false return because it is including the perl process, so you will have to filter it, but I hope you get the idea.
Related
I have a file with almost 1,500 names of Marvel heroes, each name in new line. I have to ask user what his favourite hero is and find out if it's a hero from the list or not. Here's what I have right now. It doesn't work: I can guess only the last hero from the list. For the rest it just prints that they are not on the list.
print "Whats your favourite hero?\n";
my $hero = <stdin>;
chomp $hero;
open FILE, "<list_marvel.txt";
my #marvel = <FILE>;
chomp(#marvel);
my $result = 0;
foreach (#marvel) {
if ($_ eq $hero);
}
if ($result == 1) {
print "That hero is on the list";
}
else {
print "$hero is not on the list.\n";
}
Here are two files:
-Perl code : Perl Code
-List of heroes : List
Your program has a syntax error and won't compile. It certainly won't find only the last name on the list
The main problem is that you never set $result, and if($_ eq $hero) should be something like $result = 1 if($_ eq $hero)
You must always use strict and use warnings at the top of every Perl program you write. It is an enormous help in finding straighforward problems
Here's a working version
use strict;
use warnings;
my $filename = 'list_marvel.txt';
open my $fh, '<', $filename or die qq{Unable to open "'list_marvel.txt'": $!};
print "Whats your favourite hero? ";
my $hero = <>;
chomp $hero;
my $found;
while ( <$fh> ) {
chomp;
if ( $_ eq $hero ) {
++$found;
last;
}
}
print $found ? "$hero is on the list\n" : "$hero is not on the list";
You don't set $result anywhere to true.
Make your foreach loop like this:
foreach(#marvel){
$result = $_ eq $hero;
}
or
foreach (#marvel){
$result = 1 if $_ eq $hero
}
You forgot to increment your $result. If you indent your code properly, it is easier to see.
foreach (#marvel) {
# here something is missing
if ( $_ eq $hero );
}
Add $result++ if $_ eq $hero; in the foreach.
You should always use strict and use warnings. That would have told you about a syntax error near );.
Also consider using the three argument open with lexical filehandles.
Rewritten it looks like this:
use strict;
use warnings;
use feature 'say'; # gives you say, which is print with a newline at the end
say "What's you favourite hero?";
my $hero = <STDIN>;
chomp $hero;
# alsways name variables so it's clear what they are for
my $found = 0;
# die with the reason of error if something goes wrong
open my $fh, '<', 'list_marvel.txt' or die $!;
# read the file line by line
while ( my $line = <$fh> ) {
chomp $line;
if ( $line eq $hero ) {
# increment so we know we 'found' the hero in the list
$found++;
# stop reading at the first hit
last;
}
}
close $fh;
# no need to check for 1, truth is enough
if ( $result ) {
say "That hero is on the list.";
}
else {
say "$hero is not on the list.";
}
First, you miss setting the $result at around if($_ eq $hero).
Then, you may wish to make you comparison case insensitive. This would require a regular expression, e.g.:
$result = 1 if (/^$hero$/i);
Just modified your code. After if condition increment $result. Always use use strict and use warnings and always use 3 arguments to open a file.
use strict;
use warnings;
print "Whats your favourite hero?\n";
my $hero = <stdin>;
chomp $hero;
open FILE, "<", "list_marvel.txt" or die $!;
chomp (my #marvel = <FILE>);
close FILE;
my $result = 0;
foreach my $name (#marvel)
{
if($name eq $hero)
{
$result++;
}
}
if ($result == 1)
{
print "That hero is in the list.\n";
}
else
{
print "$hero is not in the list.\n";
}
This will take a single user entry from STDIN. It will run through the file of hero names, and if one matches the user entry it will print the name and exit the loop. If the name is not found it will tell you:
use warnings;
use strict;
open my $file1, '<', 'input.txt' or die $!;
print "Enter hero: ";
chomp(my $hero = <STDIN>);
my $result = 0;
while(<$file1>){
chomp;
if (/$hero/){
print "$_\n";
$result++;
last;
}
}
print "hero not in list\n" if $result == 0;
#!/usr/bin/perl -w
foreach $arg (#ARGV) {
if ($arg eq "--version") {
print "$0: version 0.1\n";
exit(0);
} elsif ($arg =~ /-[0-9]+/) {
$n = $arg;
$n =~ s/-//g;
} elsif ($arg eq <){
print "in";
} else {
push #files, $arg;
}
}
How can I check if $arg is equal to < from input?
thanks in advance.
You correctly used quotes for the string --version. You should use quotes for <, too.
Note that you are checking command line arguments, not input. Also note that on *nix systems, if you call the script like
myscript.pl -12 < input
the < input part is processed by the shell and doesn't appear in the argument list.
See also Getopt::Long.
#hahahakebab: What #choroba said is exactly correct. When you are passing < as the command line argument put that in double quote "<" so that it takes as input to the program and modified code as below
foreach my $arg (#ARGV) {
if ($arg eq "--version") {
print "$0: version 0.1\n";
exit(0);
} elsif ($arg =~ /-[0-9]+/) {
$n = $arg;
$n =~ s/-//g;
} elsif ($arg eq "<"){
print "in";
} else {
push #files, $arg;
}
}
If < was used in the command line, then standard input is coming from a file.
If < was not used in the command line (and | is not used to specify input from the output stream of another process), then standard input is from a terminal.
You can distinguish between these two cases in Perl by checking -t STDIN
if (-t STDIN) {
print "input is from terminal\n";
} else {
print "input is from a file/pipe\n";
}
I have a script that starts of with asking the user for a filename.
I want to add a feature so that i can supply a filename in the commandline even before running the program.
To the point:
If i started my program with "perl wordcounter.pl text.txt",
How could i access the string after the name of the program (ie text.txt), from within the code?
I started doing something like:
$filename = "Filename supplied in the commandline";
if ($filename == 0) {
print "Word frequancy counter.\nEnter the name of the file that you wish to analyze.\n";
chomp ($filename = <STDIN>);
}
Basically if no textfile was supplied at the commandline, $filename would still 0 and then it would proceed to ask for a file.
Anyone know how i can access the "Filename supplied in the commandline" in the code?
try using array #ARGV
$x1 = $ARGV[0] || 'NONE';
although #ARGV is a promising option but i would rather use getopts
here is the sample code that can be used according to your need
try this perl sample.pl -file text.txt and again without file argument perl sample.pl
#!/usr/bin/perl
use Getopt::Long;
# setup my defaults
GetOptions(
'file=s' => \$file,
'help!' => \$help,
) or die "Incorrect usage!\n";
if( $help ) {
print "Common on, it's really not that hard.\n";
} else {
print "reading the $name.\n";
if ( -e $file ){
open( DATA ,'<',$file ) or die "unable to open \$file = $file\n";
while (<DATA>){
print "$_\n";
}
} else {
chomp ($file = <STDIN>);
open( DATA ,'<',$file ) or die "unable to open \$file = $file\n";
while (<DATA>){
print "$_\n";
}
}
}
~
I wrote a program to compare the image files of two folders(having 1000 files each) with some logic (see this SO question).
While executing it is comparing successfully until 900 images but then it gives an error like Use of uninitialized value within #tfiles2 in concatenation (.) or string at C:\dropbox\Image_Compare\image_magick.pl line 55 (#3).
And then I get a popup error like Perl Command Line Interpreter has stopped working, so I close the program.
My code is as follows:
#!/usr/bin/perl
use Image::Magick;
no warnings 'uninitialized';
use warnings;
use diagnostics;
#use strict;
use List::Util qw(first);
my $directory1="C:/dropbox/Image_Compare/folder1";
opendir(DIR, $directory1) or die "couldn't open $directory1: $!\n";
my #files1 = grep { (!/^\./) && -f "$directory1/$_" } readdir(DIR);
closedir DIR;
print #files1;
print 'end of files1';
my $directory2="C:/dropbox/Image_Compare/folder2";
opendir(DIR, $directory2) or die "couldn't open $directory2: $!\n";
my #files2= grep { (!/^\./) && -f "$directory2/$_" } readdir(DIR);
closedir DIR;
print #files2;
print 'end of files2';
print $files1[0];
foreach my $fils2 (#files2)
{
$g1 = Image::Magick->new;
$g2 = Image::Magick->new;
$temp1 = $g1->Read( filename=>"C:/dropbox/Image_Compare/folder1/".$files1[0]."");
$temp1 = $g2->Read( filename=>"C:/dropbox/Image_Compare/folder2/".$fils2."");
$g3 = $g1->Compare( image=>$g2, metric=>'AE' ); # compare
$error1 = $g3->Get( 'error' );
#print $error1;
if ($error1 == '0')
{
print "Matching image is:";
print $fils2 . "\n";
my $tdirectory2="C:/dropbox/Image_Compare/folder2";
opendir(DIR, $tdirectory2) or die "couldn't open $directory2: $!\n";
my #tfiles2 = grep { (!/^\./) && -f "$tdirectory2/$_" } readdir(DIR);
closedir DIR;
#my $index = firstidx { $_ eq'"' .$fils2.'"' } #tfiles2;
my $index = first { $tfiles2[$_] eq $fils2} 0..$#tfiles2;
#print $fils2;
print $index;
my $i=0;
foreach my $fils1 (#files1)
{
print 'ganesh';
print $files1[$i];
print $tfiles2[$index];
print 'gowtham'; print "<br />";
#print #tfiles2;
$g4 = Image::Magick->new;
$g5 = Image::Magick->new;
$temp2 = $g4->Read( filename=>"C:/dropbox/Image_Compare/folder1/".$files1[$i]."");
$temp2 = $g5->Read( filename=>"C:/dropbox/Image_Compare/folder2/".$tfiles2[$index]."");
$g6 = $g4->Compare( image=>$g5, metric=>'AE' ); # compare
$error2 = $g6->Get( 'error' );
$i++;
$index++;
if ($error2 == '0') {}
else {print "Image not matching:"; print $tfiles2[$index]; last;}
#if ($i == '800') {last;}
}
last
}
}
Can anyone please help, where i am doing a mistake.
Folder 1 file names: 0025.bmp to 1051.bmp;
Folder 2 file names: 0000.bmp to 1008.bmp;
Thanks
Ganesh
I don't know which the offending line is, but one of these is likely to be the candidate:
$temp2 = $g5->Read( filename=>"C:/dropbox/Image_Compare/folder2/".$tfiles2[$index]."");
or
else {print "Image not matching:"; print $tfiles2[$index]; last;}
Do note that you increment $index whether or not it is inside the array bounds. You do not check for the condition $index > $#tfiles, which should break the loop.
You might want to assert that both input arrays contain >> 900 elements, by printing the length like print "length: ", scalar #array, "\n";.
You can test at which index the undefined error actually happens by testing for definedness of the elements in the arrays:
if (not defined $tfiles[$index] or not defined $files1[$i]) {
die "There was an undefined element at index=$index, i=$i";
}
But then again, the offset between $i, and $index is constant (as mentioned in my answer), so you don't have to actually carry two variables.
A simple comparator subroutine could make your code more readable, thus aiding debugging (see procedural programming).
# return true if matching, false otherwise.
sub compare_images {
my ($file1, $file2) = #_;
my $image1 = Image::Magick->new;
$image1->Read(filename => $file1);
my $image2 = Image::Magick->new;
$image2->Read(filename => $file2);
my $result = $image1->Compare(image => $image2, metric => 'AE')->Get('error');
# free memory
undef $image1;
undef $image2;
return 0 == $result;
}
called like
my $image_root = "C:/dropbox/Image_Compare";
my ($folder1, $folder2) = qw(folder1 folder2);
unless (compare_images("$image_root/$folder1/$files1[$i]",
"$image_root/$folder2/$tfiles[$index]")) {
print "Images not matching at index=$index, i=$i\n";
print "filename: $tfiles[$index]\n";
last;
}
You could read your directories like
sub get_images_from_dir {
my ($dirname) = #_;
-d $dirname or die qq(The path "$dirname" doesn't point to a directory!);
opendir my $dir => $dirname or die qq(Can't open "$dirname": $!);
my #files = grep {!/^\./ and -f "$dirname/$_"} readdir $dir;
closedir $dir;
unless (#files) { die qq(There were no interesting files in "$dirname".) }
return #files;
}
Steps like these make code more readable and make it easy to insert checks.
I have the following program "Extract.pl", which opens a file, finds the lines containing "warning....", "info...", "disabling..." then counts and prints the value and number of them. It is working ok.
What I want to do is to create command line arguments for each of the 3 matches - warning, disabling and infos and then run either of them from the command prompt.
Here is the code:
#!/usr/bin/perl
use strict;
use warnings;
my %warnings = ();
my %infos = ();
my %disablings = ();
open (my $file, '<', 'Warnings.txt') or die $!;
while (my $line = <$file>) {
if($line =~ /^warning ([a-zA-Z0-9]*):/i) {
++$warnings{$1};
}
if($line =~ /^disabling ([a-zA-Z0-9]*):/i) {
++$disablings{$1};
}
if($line =~ /^info ([a-zA-Z0-9]*):/i) {
++$infos{$1};
}
}
close $file;
foreach my $w (sort {$warnings{$a} <=> $warnings{$b}} keys %warnings) {
print $w . ": " . $warnings{$w} . "\n";
}
foreach my $d (sort {$disablings{$a} <=> $disablings{$b}} keys %disablings) {
print $d . ": " . $disablings{$d} . "\n";
}
foreach my $i (sort {$infos{$a} <=> $infos{$b}} keys %infos) {
print $i . ": " . $infos{$i} . "\n";
}
The builtin special array #ARGV holds all command line arguments to the script, excluding the script file itself (and the interpreter, if called as perl script.pl). In the case of a call like perl script.pl foo bar warnings, #ARGV would contain the values 'foo', 'bar', and 'warnings'. It's a normal array, so you could write something like (assuming the first argument is one of your options):
my ($warning, $info, $disabling);
if ($ARGV[0] =~ /warning/i) { $warning = 1 }
elsif ($ARGV[0] =~ /info/i) { $info = 1 }
elsif ($ARGV[0] =~ /disabling/i) { $disabling = 1 }
# [...] (opening the file, starting the main loop etc...)
if ( $warning and $line =~ /^warning ([a-zA-Z0-9]*)/i ) {
++$warnings{$1};
}
elsif ( $info and $line =~ /^info ([a-zA-Z0-9]*)/i ) {
++$infos{$1};
}
elsif ( $disabling and $line =~ /^disabling ([a-zA-Z0-9]*)/i ) {
++$disablings{$1};
}
I created flag variables for the three conditions before the main loop that goes through the file to avoid a regex compilation on every line of the file.
You could also use the Getopt::Long or Getopt::Std modules. These provide easy and flexible handling of the command line arguments.