4

I invite you, tear me a new one.

This code gets the job done. It takes a .txt file containing a list of IPs and writes a file containing their respective fully qualified domain names.

I want to know in what ways is this code poorly written. What bad habits are here?

I am a perl and programming newbie. I managed to put this together using google and trail and error. Getting it to work was satisfying but please tell me how I can improve.

use strict;
use warnings;
use Socket;
use autodie;


my $filename = 'IPsForFQDN.txt';
#File with list of IPs to lookup.
#One IP address per line like so:
#10.10.10.10
#10.10.10.11
#10.10.10.12
#etc...


open(my $fh, '<:encoding(UTF-8)', $filename)
    or die "Could not opne file '$filename' $!";
my $fqdn = '';

while (my $row = <$fh>) {
    chomp $row;

    print "$row\n";
    $fqdn = gethostbyaddr(inet_aton($row), AF_INET);
    print $fqdn;
    print "\n";
    open FILE, ">>fqdn.txt" or die $!;
    print FILE $fqdn;
    print FILE "\n";
    close FILE;

}
print "done\n";

For instance is the {chomp $row;} line needed? I have NO IDEA what it does.

I am equally mystified by the whole {or die $!;} thing.

21eleven
  • 41
  • 4
  • 6
    If your code is already working, this question would be more suited for http://codereview.stackexchange.com/. – ThisSuitIsBlackNot Mar 06 '14 at 16:35
  • 3
    Mostly looks good. I don't have anything to tell you that [`Perl::Critic`](http://metacpan.org/pod/Perl::Critic) wouldn't say. – mob Mar 06 '14 at 16:44
  • 1
    It's simple enough to be a candidate for a one-liner: `perl -MSocket -nwe 'my $fqdn = gethostbyaddr(inet_aton($_), AF_INET); print $fqdn? $fqdn :"", "\n";' IPsForFQDN.txt >fqdn.txt` – R Perrin Mar 06 '14 at 18:15

3 Answers3

2

$! reports why something failed. Here if you were unable to open the file the reason for failure would be pointed out. perlvar has a section on error variables.

You're using chomp to remove the newline character from the end of each line.

When writing the file you call open slightly differently, consider using the same 3 argument version as you do when opening for reading earlier in your code (also see the link I gave you for open), and in the same coding style. It's good to be consistent, also this method is safer.

Dr.Avalanche
  • 1,944
  • 2
  • 28
  • 37
2

You're repeatedly opening fqdn.txt for every line you write. I'd just open it before the loop and close it at the end.

Oh - and you're using autodie so the or die shouldn't be necessary.

Oh - and you've used old-style open for it too, compared to new-style open for the reading file.

Richard Huxton
  • 21,516
  • 3
  • 39
  • 51
1

Not much going on at work so I had a go at a little rewrite with comments in to explain a few things. Not right/not wrong just my spin and a few of the standards we use at my place have been added.

Hope this helps...

use strict;
use warnings;
use Socket;

# initialize variables here.
my $filename = "IPsForFQDN.txt";

# open both file handles - once only
# Note safer expression using 2 commas
open(FH, "<", $filename)
        or die "Could not opne file '$filename' $!";

# open FILE for appending
open FILE, ">>", "fqdn.txt" or die $!;

# use foreach instead of while - easier syntax (may provoke discussion ;-) )
# replaced $fh for FH - use file handles throughout for consitency
foreach my $row ( <FH> )
{
    chomp $row;

    # put a regex check in for comments

    if( $row !~ m/^#/ )
    {
        printf ("Row in file %s \n", $row );

        # initialize $fqdn here to keep it fresh
        my $fqdn = gethostbyaddr(inet_aton($row), AF_INET);

        # formatted print to screen (STDOUT)
        printf ("FQDN %s \n", $fqdn);

        # formatted print to output file
        printf FILE ("%s \n", $fqdn);
    }
}

# close both file handles - once only
close FILE;
close FH;

print "done\n";
thonnor
  • 1,206
  • 2
  • 14
  • 28
  • Oh yes, in a couple of places I swapped single quotes for double quotes for consistency throughout. Also I prefer using double quotes over single - this stems from the way UNIX systems don't substitute variables in single quotes #MyIssueWithSingleQuotes :) – thonnor Mar 06 '14 at 17:03
  • 1
    As a *standard*, consider adopting lexical file handles when [open](http://perldoc.perl.org/functions/open.html)ing files, and eschew [bareword file handles](http://modernperlbooks.com/mt/2011/07/why-the-modern-perl-book-avoids-bareword-filehandles.html) (with a few exceptions). – Kenosis Mar 06 '14 at 17:13
  • 1
    How is `foreach` easier than `while`? To make things worse, you read the entire file into memory when you call `<>` in list context, which is completely unnecessary since you're just iterating through it line-by-line anyway. And as Kenosis said, use lexical filehandles (`my $fh`) instead of typeglobs (`FH`), which are global in scope. – ThisSuitIsBlackNot Mar 06 '14 at 19:26
  • I suppose when I say "easier" I mean it is my preferred way of iterating through a list. `While` is okay but I prefer using this when talking about a condition being met `(while $a = true)` I think one of Perl's strengths is its wordy synatx where you can almost almost write actual sentences `foreach $line ( @list )` As for using lexical filehandles - again typeglobs are something I have been brought up using and is my preferred syntax. It would have to be a huge list of IP's to have a major impact on memory resources. In which case testing would reveal it and a rethink would be required. – thonnor Mar 07 '14 at 01:18
  • 1
    Having your own personal preferences is fine, but you're promoting bad practices in a public forum. Perhaps you're not aware of [*why* you shouldn't use typeglobs](http://stackoverflow.com/a/3276771/176646). As for memory, if you can guarantee that your input file will always be small, even after you've stopped maintaining the code and it's being used for something you never anticipated, by all means, slurp it into memory. I can't make that guarantee about the code I write. Besides, is `while (my $row = <$fh>)` really that difficult to read? – ThisSuitIsBlackNot Mar 07 '14 at 15:34