rmd: (fightclubanimated)
[personal profile] rmd
Question for people who are better at perl than I am (this is most people who program in perl):
So, I've got a script that gets MAC addresses and bridgeport/portdescription info from switches. And there's one subroutine that cleans up the MAC address that the switch gives out. I hand the function a MAC address that's in the form xx:xx:xx:xx:xx:xx, except the switch drops leading zeroes, so it might look like "0:ab:cd:b:ee:ef" instead of "00:ab:cd:0b:ee:ef". The subroutine breaks it up and turns the single digit segments ("a") into a two character segment ("0a") and returns the prettier MAC string. There's also a global variable "prettyMAC", and if that's set, it returns the MAC in xxxx.xxxx.xxxx format instead.

Here's the code:
sub cleanMAC {
    my ($mac) = @_;
    my @explodedMAC = split(/:/,$mac);
    if ($#explodedMAC == 5) {
        my $i = 0;
        while ($i < 6) {
            if (length($explodedMAC[$i]) == 1) {
                $explodedMAC[$i] = "0" . $explodedMAC[$i];
            }
            $i++;
        }
    } else {
        print STDERR "Something is wrong - I have a MAC address I can't parse\n";
        die "Borked MAC - aborting\n";
    }
    if ($prettyMAC) {
        $mac =  $explodedMAC[0] . $explodedMAC[1] . "." . $explodedMAC[2] . $explodedMAC[3] . "." . $explodedMAC[4] . $explodedMAC[5]
    } else {
        $mac = $explodedMAC[0] . ":" . $explodedMAC[1] . ":" . $explodedMAC[2] . ":" . $explodedMAC[3] . ":" . $explodedMAC[4] . ":" . $explodedMAC[5];
    }
    return ($mac);
}


It works, but I feel like there should be a more elegant way to do this in perl. Thoughts? Suggestions?
Thanks!

Date: 2013-04-13 09:50 pm (UTC)
ceo: (Default)
From: [personal profile] ceo
perl -e 'my $mac="01:2:03:4:c:f"; map { printf("%02x:", $_); } split /:/, $mac'

(for some reason this isn't printing the hex digits, even though %x is the conversion for hex. I don't have time right now to debug this, but will look into it more later.)

Date: 2013-04-13 09:59 pm (UTC)
bryant: (Default)
From: [personal profile] bryant
%x converts int to hex notation. I did:

$mac = join(":", map { sprintf("%02x", hex($_)) } split(/:/, $mac));

Which works. I think you need the join(); your one-liner also prints a ":" at the end of the string.

Date: 2013-04-13 10:03 pm (UTC)
From: [identity profile] rmd.livejournal.com
aha! I had forgotten about the "join" function, which may be useful for me, here.

Date: 2013-04-13 10:11 pm (UTC)
bryant: (Default)
From: [personal profile] bryant
Actually, we can do better than that.

$mac = "01:2:03:4:c:f";
$mac =~ s/\b(\w)\b/0$1/g;
print $mac;

Go go regex. \b = word boundary, so \b\w\b = any single alphanumeric/connector punctuation character. Stick a "0" before it, make the regex global on the string so it matches more than once.

Date: 2013-04-14 02:09 am (UTC)
ceo: (code)
From: [personal profile] ceo
Nice!

Date: 2013-04-14 02:45 am (UTC)
From: [identity profile] charleshaynes.livejournal.com
This one is locale dependent and does (what may be) the wrong thing with e.g. "f:r:o:g:s"

All of these solutions work fine in the "happy path" but the tester in me asks "what do you want to happen with malformed inputs?"

Also JWZ re regex but also JWZ re perl! :-)

I would do it using split + hex, reconstruct the MAC as a 48 bit number

use List::Util qw(reduce);

$mac_val = reduce { $a * 256 + $b } split(/:/, $mac)

But that would be wrong. :)

Bryant has the right of it.

Date: 2013-04-14 04:57 pm (UTC)
bryant: (Default)
From: [personal profile] bryant
Well, it's a concise solution in the spirit of line noise. I think [livejournal.com profile] frobzwiththingz is correct below.

Date: 2013-04-14 03:14 am (UTC)
ext_106590: (waffle off)
From: [identity profile] frobzwiththingz.livejournal.com
Aside from your code allowing some malformed inputs to slip through (which I suppose you could accept given a sufficiently high confidence in the range of inputs your router will hand you... your call)... It's lengthy, but you just wrote a chunk of Perl code that works and was trivial to understand, scanning through it only once. There's VALUE in that; don't throw that away solely for the sake of "elegance".

Perl might let you do what you want in a single line of 24 characters, but that doesn't mean you *should*.

Date: 2013-04-14 04:11 am (UTC)
From: [identity profile] rmd.livejournal.com
I'm doing a bit of input-checking before I hand it off to this subroutine, and all I really do with the macs is get them along with the bridgeport ID, and then turn around and match the bridgeport ID to the port description - all the MAC address does is just show up and get printed out; I'm not relying on it for things further downstream.

And, yeah, it's easily understandable to me as-is. One thing I like about it is in a year or two when I'm cannibalizing this code to write a different script, I'm likely to still understand it. (I actually hacked together the full script, then looked at it and realized it was slightly incomprehensible even when it was all fairly fresh in my mind, at which point I went back and wrote it again putting it into a more coherent format.)

But I realized a while ago that one of the venues for getting better at this sort of thing is to put code out and get feedback on it. So, I am.

I'm unlikely to change the running code, but I figure it's one way to give myself more options for the next script I end up writing.

Date: 2013-04-15 12:44 pm (UTC)

Profile

rmd: (Default)
rmd

June 2025

S M T W T F S
1234567
89 1011121314
15161718192021
22232425262728
2930     

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 18th, 2026 03:21 pm
Powered by Dreamwidth Studios