|
As the title suggests, there is always more than one way. For a popular scripting language like perl, the number of variations can lead to both better and worse code. Others, like bash and expect, have certain limitations that make it a bit more cumbersome for some undertakings but, with a bit of logic, there are usually workarounds (never let it be said that "until" is a necessary conditional function when there exists "while").
Taking the MAC Generators for Xen that Allen and Chris wrote, I wanted to expand it to be more generic and illustrate some of the strengths and differences between the scripts. We also explore some different methods on how to extract valid hexadecimal pairs. As far as I can tell, randomizers are less likely to repeat a sequence when you generate them consecutively instead of looping them repeatedly, which causes the randomizer to reseed on each pass (if the seed is duplicated, so is the output).
In python, there is the ability to create loops within lists that gives us the advantage of a more compacted generate mechanism, whereas the other languages will require sorting through an array. The triple-for loop can be written as one line, but I separated it into 3 for display purposes.
python:
#!/usr/bin/python # Generate unique MAC addresses
from random import randint from re import findall from string import upper from sys import stdin, exit
max = 6 # max number of hex pairs sep = ':' # mac field separator xen = '00:16:3e' # change this if not XEN
# ask for user input without newline print '\nSpecify prefix [%s]: ' % xen, pre = stdin.readline()[:-1] if pre is '': pre = xen
# only extract hex pairs from input, discard singles and all else pairs = findall('[0-9A-Fa-f]{2}', pre) if len(pairs) > max: print 'MAC Addresses cannot be longer than %d hex pairs' % max exit(1) pre = sep.join(['%02x' % int(p,16) for p in pairs]) if len(pairs) == 0: print 'Generating full MAC without prefix' else: print 'Using prefix ' + pre pre += ':'
# loop until user enter a valid numerical response num = [] while len(num) == 0: print '\nHow many MACs do you want to generate? ', num = findall('^\d+, stdin.readline()) print num = int(num[0])
# generate random list using the most compacted method possible for mac in [ sep.join(['%02X' % randint(0,255) for p in range(max - len(pairs))]) for r in range(int(num)) ]: print upper(pre) + mac
In bash, the "#%/" variable manipulation lacked the complexity of other languages. I had almost given up and resorted to sed:
sed -re 's/[0-9A-Fa-f]{2}/& /g;s/(^| )[0-9A-Fa-f]??[^0-9A-Fa-f]+/ /g;' )
But then figured out a workaround with a while/case hack.
bash:
#!/bin/bash # Generate unique MAC addresses
max=6 # max number of hex pairs sep=: # mac field separator xen=00:16:3e # change this if not XEN
# ask for user input without newline echo read -p "Specify prefix [$xen]: " pre pre=${pre:=$xen}
# only extract hex pairs from input, discard singles and all else declare -a pairs=( ) while [[ ${#pre} -gt 0 ]]; do case $pre in [0-9A-Fa-f][0-9A-Fa-f]*) pairs[${#pairs[*]}]=${pre::2} pre=${pre#??} ;; *) pre=${pre#?} ;; esac done len=${#pairs[*]} pairs="${pairs[*]}" if [[ $len -gt $max ]]; then printf ' MAC Addresses cannot be longer than %d hex pairs\n' $max exit 1 fi pre=${pairs// /$sep} pre=${pre#$sep} if [[ $len -eq 0 ]]; then echo -e " Generating full MAC without prefix" unset pre else echo -e " Using prefix $pre" fi
# loop until user enter a valid numerical response set -- declare -i num="${1:-0}" until [[ $num -gt 0 ]]; do echo read -p "How many MACs do you want to generate? " num done echo
# generate the list using the most compacted method possible for ((r=0; r<$num; r++)); do echo -n ${pre%$sep} | tr [:lower:] [:upper:] for ((p=$[max-len]; p>0; p--)); do [[ $p -ne $max ]] && echo -n $sep printf "%02X" $((RANDOM % 256)) done echo done
perl:
#!/usr/bin/perl # Generate unique MAC addresses
my $max = 6; # max number of hex pairs my $sep = ':'; # mac field separator my $xen = '00:16:3e'; # change this if not XEN
# ask for user input without newline printf "\nSpecify prefix [%s]: ", $xen; my $pre = ; chomp $pre; $pre = $xen if (!$pre);
# only extract hex pairs from input, discard singles and all else my @pairs = (); while ($pre =~ m/([0-9A-Fa-f]{2})/) { push(@pairs, ); $pre =~ s/^.*//; } if (scalar(@pairs) > $max) { printf " MAC Addresses cannot be longer than %d hex pairs", $max; exit 1; } $pre = join(":", @pairs); if (scalar(@pairs) == 0) { print " Generating full MAC without prefix\n"; } else { print " Using prefix $pre\n"; }
# loop until user enter a valid numerical response my $num = 0; while ($num <= 0) { print "\nHow many MACs do you want to generate? "; $num = ; } print "\n";
# generate random list using the most compacted method possible for (my $r=0; $r<$num; $r++) { print uc($pre); for (my $p=($max-scalar(@pairs)); $p>0; $p--) { print $sep if ($p != $max); printf "%02X", rand(255); } print "\n"; }
expect:
#!/usr/bin/expect # Generate unique MAC addresses
exp_version -exit 5.0 exp_internal 0 log_user 0 set timeout 15
set max 6; # max number of hex pairs set sep :; # mac field separator set xen 00:16:3e; # change this if not XEN
# ask for user input without newline send_user [ format "\nSpecify prefix \[%s\]: " $xen ] expect -re "(.*)\n" { set pre $expect_out(1,string) } if { $pre == "" } then { set pre $xen }
# only extract hex pairs from input, discard singles and all else set pairs [ regexp -all -inline -- {[0-9A-Fa-f][0-9A-Fa-f]} $pre ] set len 0 foreach p $pairs { incr len } if { $len > $max } { send_user " MAC Addresses cannot be longer than $max hex pairs\n" exit 1 } set pre [ join $pairs $sep ] if { $len == 0 } { send_user " Generating full MAC without prefix\n"; } else { send_user " Using prefix $pre\n"; }
# loop until user enter a valid numerical response send_user "\nHow many MACs do you want to generate? " expect -re "(\\d+)\n" { set num $expect_out(1,string) } send_user "\n"
# generate random list using the most compacted method possible for {set r 0} {$r<$num} {incr r} { send_user [ string toupper $pre ] for {set p [expr $max-$len]} {$p>0} {incr p -1} { if { $p != $max } { send_user $sep } send_user [ format "%02X" [expr int(rand()*255)] ] } send_user "\n" }
php:
#!/usr/bin/php <?php // Generate unique MAC addresses
$max = 6; # max number of hex pairs $sep = ':'; # mac field separator $xen = '00:16:3e'; # change this if not XEN
// ask for user input without newline printf ("\nSpecify prefix [%s]", $xen); $pre = readline(": "); if (!$pre) { $pre = $xen; }
// only extract hex pairs from input, discard singles and all else preg_match_all('/[0-9A-Fa-f]{2}/', $pre, $pairs); if (count($pairs[0]) > $max) { printf (" MAC Addresses cannot be longer than %d hex pairs\n", $max); exit(1); } $pre = join($sep, $pairs[0]); if (count($pairs[0]) == 0) { print " Generating full MAC without prefix\n"; } else { print " Using prefix $pre\n"; }
// loop until user enter a valid numerical response $num = 0; while ($num == 0) { $num = intval(readline("\nHow many MACs do you want to generate? ")); } print "\n";
// generate random list using the most compacted method possible for ($r=0; $r<$num; $r++) { print strtoupper($pre); for ($p=($max-count($pairs[0])); $p>0; $p--) { if ($p != $max) { print $sep; } printf ("%02X", rand(0,255)); } print "\n"; }
?>
Spent about two hours between last night and this morning and learned myself a little bit of ruby or, at least, enough to convert this script to ruby. I cannot guarantee that it is the most efficient, but it produces the same exact output as the rest:
ruby:
#!/usr/bin/ruby # Generate unique MAC addresses
max = 6 # max number of hex pairs sep = ':' # mac field separator xen = '00:16:3e' # change this if not XEN
# ask for user input without newline printf("\nSpecify prefix [%s]: ", xen) pre = readline.chomp if pre.empty? pre = xen end
# only extract hex pairs from input, discard singles and all else pairs = pre.scan(/[0-9A-Fa-f]{2}/) if pairs.length > max printf(" MAC Addresses cannot be longer than %d hex pairs\n", max) Process.exit 1 end pre = pairs.join(sep) if pairs.length == 0 puts ' Generating full MAC without prefix' else puts ' Using prefix ' + pre end
# loop until user enter a valid numerical response num = nil while num == nil printf("\nHow many MACs do you want to generate? ") num = Integer(readline.chomp.scan(/^\d+$/)[0]) end puts
# generate random list using the most compacted method possible p = Array(1..(max-pairs.length)) Array(1..num).each { if pairs.length > 0 printf("%s:", pre.upcase) end p.collect! { |r| sprintf('%02X', Integer(rand*255)) } puts p.join(sep) }
|