http://www.perlmonks.org?node_id=358176

Net::Ping
"icmp socket error at foo.pl line..."

Windows XP sp2, Activeperl 5.8.6
Logged in as "administrator"
Any and all firewalls disabled
But ping.exe works fine.
And scriplet below works fine on Win2Kpro, and also Debian Linux.

#!/usr/bin/perl use warnings; use strict; use Net::Ping; # use Module::Versions::Report; my @pinglist = ( 'localhost', 'www.google.com', ); # my $p = Net::Ping->new('icmp', 5); # my $p = Net::Ping->new('icmp', 5) or die "$!\n"; # my $p = Net::Ping->new('icmp', 5) or die "0+$!\n"; my $p = Net::Ping->new('icmp', 5) or die "$^E\n"; # for my $h (@pinglist) { # if ($p->ping($h, 5)) { print "(ok) $h"; } # else { print "<NO> $h"; } # print "\n"; # } $p->close;
##########################################################################

Proposed for inclusion with existing PM themes like Blue, Red, Green, Dark, Jblue, Nightshift

/* Nominal LarryWall Theme */ /* for use with Blue, Green, or Red themes */ body {background-color:#e0ff00;} A:link {color:#0000ff; background-color:inherit} A:visited {color:#800080; background-color:inherit} A:active {color:#ff0000; background-color:inherit}

##########################################################################

Convenience tweaks pertaining to jdporter's Automate your PerlMonks activities

# ~/.bash_profile if [ -d ~/bin/Pmt ]; then export PM_USER=ybiC PATH="~/bin/Pmt:${PATH}" fi #!/bin/sh # ~/bin/PmtMp cd ~/bin/Pmt reset multipoll #!/bin/sh # ~/bin/Pmt/Ou while [ 1 ] do clear date other_users sleep 120 done #!/bin/sh # ~/bin/Pmt/Ssm clear shell send_msg #!/bin/sh # ~/bin/Pmt/Cb clear chatterbox -p 17

##########################################################################

Consulting wisdom from brother pjf:

I've encouraged him to post this as a Meditation, but methinks he be currently far too busy for much PM participation.

There's a topic that I've seen arise on the chatterbox a number of times, and that's the issue of freelance consulting. How to do it, what rates to charge, and what gotchas there exist in the works. During one such chatterbox conversation, ybiC asked if I could write a few words on the topic, as he and others might find them useful.

This is a (*ahem*) short discussion about consulting, particularly my experiences working as a consultant. Some of what's said in here might be useful to you, and some might be entirely the wrong advice for the situation at hand. Like any advice, you should take it with a sprinkle of salt.

Before you start.
Before you start consulting, be aware that it's not the high-income, low-work, relaxing lifestyle that often it's thought to be. It can be all those things if you're lucky or play your cards right, but it can also be none of them. Consulting involves accounting, project management, client liason, tax matters, invoicing, and luck. Most jobs you try for simply won't get off the ground, or will take forever to start. When I first started writing this I was travelling out to Ballarat to work on a job which was first discussed over eight months ago, and stayed up last night working on contracts for a project that's been brewing for over two years.

Having said that, most people end up in consulting by chance, rather than choice. Let's look at the most common way this starts.

XYZ has asked me to do a project. What do I do now?
This is where most people start with consulting, it's certainly where I started very many years ago. It's also the question I most commonly see on the chatterbox.

One day, someone comes to you and asks if you're available for a particular project, you express your interest and discuss the matter with them. It's something that you're capable and well-suited to doing, but you've never done consulting like this before. After a while, the question comes up about money. How much will this cost? How soon can it be ready?

The matter of cost is a difficult one to answer. As a general rule, you want to figure out how much you'd like to be paid on a per-hourly basis. If you don't know what that should be, then take what you get paid for your "regular job", and double it. This may seem like a lot, but unless your workplace is run by facists, you normally get paid to do all sorts of things besides your main line of work, such as sleeping in, or attending critical but useless meetings. When people hire you as a consultant, they don't expect to pay for you to sleep in, or stare sleepily into that monitor after that all-night Quake game. Consultants are also a convenience, they are hired for a job, and disspaear once it's gone. As such, your customers should be prepared to pay you a little extra for your time.

Can you give me a quote?

Most clients don't care about your hourly rate, they care about your total cost. If you can get a client to pay you by the hour, then you're laughing, as it's very hard to go wrong with such an agreement. Sometimes clients can be scared by your hourly rate, and judging whether or not this will be the case is difficult to tell.

Unless you're very fortunate, your client will want a quote (either written or verbal) of some kind for the total project cost.

This is where everything gets hairy. As soon as you give a quote, most clients will be extremely reluctant to ever pay above that. That wouldn't usually be a problem if your client has told you everything you need to know, but the chances of that occuring are in the same league as being struck by lightning. It does happen, but it's a bit of a shock when it does.

So, you've given your quote, the client is happy, work commences, and half way through the project the client asks when the flash animation and musical score for their website is going to be finished, since they're doing their product launch on Tuesday. You rightly claim that you know nothing of this, the client insists that musical scores are an obvious requirement for a conference registration form, and the relationship goes downhill from there.

The key is to make sure that your quote is in writing, covers what you will (and possibly will not) do, and a list of acceptance critera, as well as the price. You also would like your client's signature on there somewhere. A well-written quote is your best defense against mis-understandings, and should be written in conjunction with your client. Find out as much as you can about what they want, ask them lots of questions, and suggest alternatives. You want to be quoting on what the client really wants. Some clients won't really know what they want, so you'll have to provide a little guidance. Some clients are incredibly sure. Some of you with software engineering backgrounds should already have experience here. Some clients won't really know what they want, so you'll have to provide a little guidance. Some clients are incredibly sure, although they might change their mind later.

If you think that writing quotes is a lot of work, you're right. You can try avoiding it, but unless you know your client extremely well, and have worked with them in the past, expect for at least one of you to be dissapointed by the end of it. Dissapointed clients never come back, and repeat customers form a significant revenue stream for consultancies. Obviously the larger the project, the more essential it is

Unfortunately, it's at the quoting stage of the process that most of your clients (or you) will lose interest. Writing and reviewing quotes is dull and boring stuff, and nobody likes to do it. Your client may realise there are many more things they wish to think about, or in the light of the quoting process the project is much more expensive or difficult than they previously thought.

One way of getting around the problem of failing client interest is to break the project up into stages, with the end of the first stage being a usable but humble product. The client may be alarmed at the full cost of the project, whereas they could be quite comfortable with the costs and benefits of the stripped down "stage 1".

Another thing worth mentioning about quoting or any other kind of client management, is that it's worth doing everything promptly. A project where the ball is always moving and the client always has a quick response is much more likely to be successful. If you're busy then a short note to say that you've received the query and an indication of when you'll been attending it is often appreciated.

Quoting styles
    "Never ever ever do fixed price work."
    --- Kirrily "Skud" Robert.

As a rule of thumb, that's good advice. Most starting consultants have a tendancy to quote low, and it's only afterwards that they discover that things are more difficult, or the goal-posts have moved. A fixed price quote makes it difficult to do a good job, you have a strong incentive to refuse extra features or do more work than necessary, in order to avoid time blow-outs which you won't be compensated for. This is bad for you and bad for your client.

The preferred quoting style I use when it's impossible or undesirable to negotiate an hourly rate is what's called a "ranged quote". A ranged quote breaks a project up into sections and gives a price range on each section. You can guarantee that the cost of each section will fall within this range. Because the total cost is now flexible, you now have much greater freedom to accept new features that appear part-way through, and you're likely to be compensated for any extra work that needs doing. If everything goes smoothly, the client gets the project at a lower cost, which will make them very happy. You can still blow-out on time with a ranged quote, so be careful.

The prototype
One of the greatest powers at your disposal is the prototype, a mock-up of the final system with some superficial whistles and bells to show how it works. Clients go nuts over these things, a prototype can really impress and bolster a lot of confidence.

The only catch with prototypes is that clients invariably feel that once they've seen a prototype, the project is almost completed. Prototypes are like cars with no electronics and nothing under the hood, except that with most projects the client isn't able to "look under the hood" and see how much work is left to be done.

Some words on clients...
There are many different types of clients in the world, and they all have different expectations. Ma and Pa's fish'n'chip shop will behave very differently to a national telco, and treating one like the other will get you nowhere. Make sure that you treat your client appropriately, as many simply won't take you seriously otherwise.

Also, be prepared for clients who are just plain bad. Some people may meet with you to get your recommendations, but then do the work in-house based upon what you've said. Some clients will dissapear when it comes to paying money. Some clients mean well, but will move project requirements in the same way that a taxi moves around town.

If you've never worked with a client before, you might want to ask for a deposit (anything from 10-50%) up-front, before the project begins. For a large project, setting milestones and agreeing upon payments for each of them is a very wise idea. Most clients who do a runner are far away, and only run once the project has been delivered. Consider demonstrating the product to the client without delivering it to them until final payment is made. This "look but don't touch" approach is often a sure-fire way of getting money from lazy payers.

##########################################################################

(copied from fsn's scratchpad)

Regarding Exchange and SMTP, ybiC, I might have given you false hopes here, but I'll try my best.

My setup was different from yours, as I didn't actually run the Exchange system, I ran the Sendmail gateway/virusscanners that were in front of it, connecting to the Internet. This was for a large, international corporation, and we fronted a lot of different internal mail systems in addition to the Exchange systems, ie. Lotus Notes, Sendmail, OpenMail etc. A mess.

Now to your problem. I think you are either having a protocol problem or a server selection problem. Some questions:

> telnet mail.myfineserver.com 25
Escape character is '^]'.
220 mail.myfineserver.com ESMTP Welcome to my fine server
helo client.myfineserver.com
250 mail.myfineserver.com Hello client.myfineserver.com 1.2.3.257, pleased to meet you
mail from: me@myfineserver.com
250 2.1.0 me@myfineserver.com... Sender ok
rcpt to: you@myfineserver.com
250 2.1.5 you@myfineserver.com... Recipient ok
data
345 Enter mail, end with "." on a line by itself
This is just a test, please ignore.
.
250 2.0.0 g6IAv6e18633 Message accepted for delivery
quit
221 2.0.0 mail.myfineserver.com is closing connection.

Hope this can help you, and if you need any more help, you are more than welcome to msg me again.

Even though I'm close to begin a rant here, I would like to comment on the script itself. In my opinion, a MUA like this script should always deliver to a server on the local machine. In other words, the $smtp = new Net::SMTP($target) should always use localhost as $target. The reason is that if you go directly to a remote server from a non-persistent script like this, you loose all the fine redundancy built into SMTP and DNS. Deliver all mails to a local SMTP server, and let it handle things like resending to next server in MX list, requeueing etc. etc. And this also is true if your local site has more than one SMTP server for redundancy. At the very least, I think $target should be fixed to a local server the you or your organization has control over.

##########################################################################

How to build a real multi-level dispatch table and Re: How to build a real multi-level dispatch table are also worth perusing

ybiC: Any comments on this article subrefs and dispatch tables?

wog: &$sref; is not the same as &$sref() in a big way. Also that one can use {}s like &{$sref} should be mentioned.

wog notes that &$sref() is the one equivilent to $sref->() of course.

ybiC: Would you say the example of dispatch table in that article is clean perl? If so, I can think of a script or two of mine that might could stand a few fewer if/else's.

Zaxo: looks ok to me, haven't checked details

Petruchio: Well, I prefer lexically scoped subroutines to lexically scoped references to package subroutines, where appropriate...

Petruchio: So right off, I would be more inclined to say my $foo = sub { print "Foo!\n" }; where I didn't have to call the sub from another package.

Petruchio: Particularly if you're going to be calling such things in lexicals anyway.

ChemBoy: $main::{"lexically scoped subs"}++

Petruchio: Dispatch tables are fine things, and can indeed simplify your code. The way they're doing it in that article may be the most appropriate way, depending on the situation.

tilly: If you always use lexically scoped subs, then strict.pm catches typos in subroutine names!

Petruchio: Well, let's say *an* appropriate way. The 'most' is always a bold statement. :-)

tilly: Oh, and you get nested subroutines! (No more naming fights between lexically scoped and global names.)

ybiC: So could one use a dispatch table with $foo instead of \&foo then? Or is that statement utter drivel?

Petruchio: Well, yes... except that my next suggestion was going to be to put them directly into a hash. :-) Strict won't, of course, help you there.

Zaxo: ybiC it's true

tilly: One could use a dispatch table with $foo. In fact one can even build up the dispatch table with functions. I have written quite a few functions that return hashes of key/handler, key/handler.

Petruchio: Yes, ybiC... by saying my $x = \&foo; you make a lexical $x hold a reference to a package subroutine called &foo. If you never need to use the subroutine as a package subroutine, you'd might as well not create it that way in the first place

Zaxo: also, you can define anon subs directly in the hash: %dt=(foo=>sub{join"bar",@_})

Petruchio: But then, I'm a bit unusual for preferring, by default, an anonymous sub in a lexical variable to a package variable. You won't find most people doing that. I think that should be made clear before I recommend my preference.

Zaxo suddenly think Petruchio already said that

Petruchio: Zaxo is correct. But as someone just mentioned, long subroutines will create formatting problems if you try to put them all into the hash declaration. This is why I often first declare my %sub; and then on later lines declare the subs.

Petruchio: I've put an example of what this looks like on my scratchpad

#!/usr/bin/perl -w use strict; { my %sub; $sub{foo} = sub{ whatever; }; $sub{bar} = sub{ whatever; whatever; }; $sub{bat} = sub{ whatever; }; $sub{moo} = sub{ whatever; whatever; whatever; }; }

##########################################################################

http://www.alvestrand.no/objectid/top.html

#! /usr/bin/perl -w # netsnmpt.pl use strict; use Net::SNMP(qw(snmp_event_loop oid_lex_sort)); my @targets = @ARGV; my @oid = ( '.1.3.6.1.2.1.2.2.1.1', '.1.3.6.1.2.1.31.1.1.1.1', '.1.3.6.1.2.1.2.2.1.7', '.1.3.6.1.2.1.2.2.1.8', ); # ifIndex # ifName # ifAdminStatus # ifOperStatus my ($session, $error, $response); for my $target(@targets) { ($session, $error) = Net::SNMP -> session( -hostname => $target, -community => 'public', ); print($session -> hostname(), "\n"); for my $oid(@oid) { if (defined($response = $session -> get_table($oid))) { for my $value(oid_lex_sort(keys(%{$response}))) { print($response -> {$value}, "\n"); } } else { print($session -> error(), "\n"); } } }

##########################################################################

Oversimplified networky terms and descriptions:

JACK is a layer 1 (physical) network receptacle.   ie; cubicle jack

PORT is a layer 2 (datalink) network receptacle.   ie; switch or hub port

INTERFACE is a layer 3 (network) network receptacle.   ie; router interface

LINK is the wired/fibered connection between two electronic network devices.

FRAME is the layer 2 unit of network data.   ie; switches pass frames.

PACKET is the layer 3 unit of network data.   ie; routers forward packets.

BANDWIDTH is the maximum number of bits that can traverse a given link in one second: Gbps, Mbps, Kbps, bps.   <update> Bandwidth limitations are usually a problem for low-speed WAN links and for apps like SCP and FTP where many large packets are crossing the wire.   Bandwidth is not to be confused with Bus Speed which is measured in Bytes per second; GBps, MBps, KBps, Bps.   Network links can be thought of as *serial* connections, as opposed to computer internal busses which are 8 or more bits wide.</update>

LATENCY is the delay added for packet to traverse a link, or by a router interface in transmitting a packet onto a link, or receiving a packet from a link.   Is greatest problem when distance between devices is large (speed of light), and for low speed links (insertion delay), and for applications like SSH and telnet where a mess o' small packets are flying about.

HUBs repeat all received frames to all other ports.   So they share internal bandwidth amongst all connected devices, but add no latency to passing frames.   Unless of course bandwidth utilization is saturated and collisions and/or drops occur.   Same IP subnet across all hub ports.   Generally appropriate for segment of >= 50 PCs with fairly light traffic levels.

SWITCHes dedicate full wirespeed bandwidth to each port, and have backplane bandwidth far exceeding any one port.   But not necessarily meeting bandwidth of all ports together.   No latency added to passing frames unless bandwidth utilization of the port is saturated, so that buffering and/or drops occur.   Note that switchport saturation can normally only occur on *uplink* ports where multiple load from multiple devices is aggregated into one port.   Same IP subnet across all switch ports.   Generally appropriate for up to around 200 PCs depending on broadcast traffic levels.

ROUTERs can add significant latency, as the CPU has to process every packet.   Generally used in LANs to interconnect hub or switch segments/subnets.   Routers limit scope of broadcast domain for switched LAN, and also to limit scope of collision domain in hubbed LAN.   Different IP subnet on each router interface.

BANDWIDTH SATURATION occurs when more traffic is generated than can be passed/forwarded by the hub/switch/router.

HALF-DUPLEX vs. FULL-DUPLEX refers to whether a port/interface can transmit and receive simultaneously.   All hub ports are limited to HDX by their very design.   Switch ports can be HDX or FDX, depending on quality of the switch, and the same for router interfaces.   FDX provides possibility of significantly better throughput than HDX since acknowledgments don't interupt flow of data packets.

##########################################################################

Planned family of Perl modules for Cisco switch+router maintenance

     Cisco
       |
       `-- Utils
             |-- Bandwidth.pm
             |-- Gui.pm
             |-- SetPass.pm
             |-- ShoIfStat.pm
             |-- ShoErrDis.pm
             |-- ShoArpT.pm
             |-- ShoCamT.pm
             |-- ShoCdpN.pm
             |-- ShoIpRoute.pm
             |-- ShoSerNum.pm
             |-- ShoVer.pm
             |-- TestSuite.pm
             `-- Internal
                    |-- DateTime.pm
                    |-- PrintLogConsole.pm
                    |-- GetFromUser.pm
                    |-- Unique.pm
                    |-- WrapSnmp.pm
                    `-- WrapTelnet.pm

Code to extract the modules from

Existing modules+libs possibly needed

Questions about modules

  1. Is it bad form to have lexical vars of same name/purpose in multiple functions of one module?
  2. Pros/cons of several single-purpose modules vs fewer number of somewhat consolidate modules?
  3. Somewhere I got the impression that OOP is a mainstay of perl modules. The scripts I am extracting code from to make these modules are mostly procedural.   Might this pose problems?
  4. Advice on writing a family of modules?

tye 'I would run h2xs separately for each module and steal the top-level build script from some dist that already has one (: (libwin32, for example).   I always end up reading source code in lib/ExtUtils when I am making modules, tho'

crazyinsomniac
Q1: not really (unless they do reference radically different things, in which case it might get confusing. remember, self documenting code)
Q3: if you need to be carrying around something like a socket, you wanna go oo...
Q4: don't forget to write Cisco::Util::TestSuite

toma - http://tomacorp.com/perl/xs.html

http://www.nevis.columbia.edu/~winter/howto/howto_create_e910_perlmod.html

Info (mostly PM nodes) on writing modules

Existing modules+libs possibly needed for GUI dev

Info on building GUI for Perl code

DateTime.pm

package Cisco::Utils::Internal::DateTime; $VERSION = 0.02.00; @EXPORT_OK = qw(DateTime); use strict; use base 'Exporter'; sub DateTime { my($sec,$min,$hour,$mday,$mon,$year) = localtime(time); my $datetime = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec ); } 1; =head1 NAME DateTime =head1 SYNOPSIS DateTime is intended for use by Cisco::Utils modules. It's not meant for use directly from Perl scripts, and it's interface may chang +e at any time without notice. DateTime accepts no arguments, and returns a scalar containing year, month, mday, hour, minute, second in human-readable format. YYYY-MM-DD hh:mm:ss #!/usr/bin/perl -w use strict; use Cisco::Utils::Internal::DateTime qw(DateTime); my $datetime = DateTime() or die "Error: $!"; print "$datetime\n"; =head1 UPDATE 2001-11-18 07:40 CDT Original working code. =head1 TODO Figure out enough h2xs to package this module for distribution. =head1 BUGS None that I know of. =head1 REQUIRES Nothing aside from Perl itself. =head1 AUTHOR ybiC =head1 CREDITS Thanks to Petruchio for kickstarting me into modulitazing my Ciscoey scripts. And to vroom for PerlMonks. =cut

GetFromUser.pm

package Cisco::Utils::Internal::GetFromUser; $VERSION = 0.05.20; @EXPORT = qw(GetFromUser); use strict; use base 'Exporter'; use Term::ReadKey; use Term::ReadLine; sub GetFromUser { my $echo = shift(@_); my $timeout = shift(@_); my @prompt = @_; my $pass; $echo = 0 unless $echo == 1; $timeout = 60 unless(($timeout =~ /\d+/) and ($timeout >= 1)); @prompt = '' unless defined @prompt; ReadMode('noecho') if $echo == 0; print $_ for @prompt; if ($^O eq 'MSWin32') { chomp ($pass = <STDIN>); } else { $pass = ReadLine($timeout); } ReadMode('restore') if $echo == 0; unless(defined($pass)) { return 'Sorry, you waited too long to enter something.'; } chomp $pass; return $pass; } 1; =head1 NAME GetFromUser =head1 SYNOPSIS GetFromUser is intended for use by Cisco::Utils modules. It's not meant for use directly from Perl scripts, and it's interface may chang +e at any time without notice. GetFromUser accepts 2 scalars and 1 array for input values: 1 - echo-to-screen: 1 for yes, 0 for no (default of no) 2 - timeout seconds: integer greater than zero (default of 60) 3 - prompt: text to prompt user to provide input (default of + '') User prompt is here so echo disabled *before* user is prompted for inp +ut. Term::ReadKey provides no-echo while user typing password. Term::ReadLine provides timeout if wait too long. Win32 doesn't support timeout, 2nd arg still required before prompt te +xt. #!/usr/bin/perl -w use strict; use Cisco::Utils::Internal::GetFromUser; my $echo = 0; my $timeout = 120; my $favColor = GetFromUser( $echo, $timeout, "\nThe old man at the ropebridge asks\n", 'Wots yer favorite color? ', ); if $favColor eq 'Sorry, you waited too long to enter something.' { print "\nWooooaaahh!!\n\n" } else { print "\n$favColor, eh? You may pass.\n\n"; } =head1 UPDATE 2001-11-18 22:20 CDT Original working code. =head1 TODO Investigate Term::Prompt for cross-platform no-echo. Figure out enough h2xs to package this module for distribution. =head1 BUGS None that I know of. =head1 REQUIRES Term::ReadKey Term::ReadLine =head1 AUTHOR ybiC =head1 CREDITS Thanks to Petruchio for kickstarting me into modulitazing my Ciscoey scripts. And to vroom for PerlMonks. =cut

PrintLogConsole.pm

package Cisco::Utils::Internal::PrintLogConsole; $VERSION = 0.02.09; @EXPORT_OK = qw(PrintLogConsole); use strict; use base 'Exporter'; sub PrintLogConsole { my ($file, $aRefMssgItems) = @_; open(FH, "> $file") or return "Error opening $file: $!\n"; for(@$aRefMssgItems){ print "$_\n"; print(FH "$_\n") or return "Error printing to $file: $!\n"; } close FH or return "Error closing $file: $!\n"; } 1; =head1 NAME PrintLogConsole =head1 SYNOPSIS PrintLogConsole is intended for use by Cisco::Utils modules. It's not meant for use directly from Perl scripts, and it's interface may chang +e at any time without notice. PrintLogConsole accepts a list of two elements. The first is a scalar of the file to print to. The second is an array reference of the message(s) to print. #!/usr/bin/perl -w use strict; use Cisco::Utils::Internal::PrintLogConsole qw(PrintLogConsole); my $logfile = './file.log'; my @msgs = ( "Starting program run", "La-la-la...", "Humina humina...", ); my $response = PrintLogConsole($logfile, \@msgs); unless ($response = 1) { print $response; exit; } =head1 UPDATE 2001-11-17 22:25 CDT Original working code. =head1 TODO Figure out enough h2xs to package this module for distribution. =head1 BUGS None that I know of. =head1 REQUIRES Nothing aside from Perl. =head1 AUTHOR ybiC =head1 CREDITS Thanks to Petruchio for kickstarting me into modulitazing my Ciscoey scripts. And to jcwren for scalar+array passing tips. And to vroom for PerlMonks. =cut

Unique.pm

package Cisco::Utils::Internal::Unique; $VERSION = 0.15.02; @EXPORT_OK = qw(Unique); use strict; use base 'Exporter'; use Tie::IxHash; sub Unique { my @inlist = @_; my (@uniq, %seen); my $have_TIxH; BEGIN { $have_TIxH = 0; eval { require Tie::IxHash }; unless ($@) { Tie::IxHash->import(); $have_TIxH = 1; } } tie (%seen, "Tie::IxHash") if ($have_TIxH == 1); ++$seen{$_} for(@inlist); @uniq = keys %seen; } 1; =head1 NAME Unique =head1 SYNOPSIS Unique is intended for use by Cisco::Utils modules. It's not meant for use directly from Perl scripts, and it's interface may chang +e at any time without notice. Unique accepts a list, and returns a list containing only the unique elements. If Tie::IxHash is installed, the return elements are in the same order as the list input. If not, the order of the returned list elements are subject to Perl's unpredictable hash element ordering. #!/usr/bin/perl -w use strict; use Cisco::Utils::Internal::Unique qw(Unique); my $file = shift or die "You forgot to provide a filename!\n"; open (FH, "< $file") or die "Error opening $file for read: $!"; my @lines = <FH>; close FH or die "Error closing $file: $!"; my @uniques = Unique(@lines) or die "Error: $!"; print "$_" for @uniques; =head1 UPDATE 2001-11-17 19:15CDT =head1 TODO Check for presence of Tie::IxHash before tieing %seen. =head1 BUGS None that I know of. =head1 REQUIRES Tie::IxHash =head1 AUTHOR ybiC =head1 CREDITS Thanks to Petruchio for kickstarting me into modulatizing my Ciscoey scripts. And to vroom for PerlMonks. =cut