📄 ch03_12.htm
字号:
<HTML><HEAD><TITLE>Recipe 3.11. Program: hopdelta (Perl Cookbook)</TITLE><METANAME="DC.title"CONTENT="Perl Cookbook"><METANAME="DC.creator"CONTENT="Tom Christiansen & Nathan Torkington"><METANAME="DC.publisher"CONTENT="O'Reilly & Associates, Inc."><METANAME="DC.date"CONTENT="1999-07-02T01:30:48Z"><METANAME="DC.type"CONTENT="Text.Monograph"><METANAME="DC.format"CONTENT="text/html"SCHEME="MIME"><METANAME="DC.source"CONTENT="1-56592-243-3"SCHEME="ISBN"><METANAME="DC.language"CONTENT="en-US"><METANAME="generator"CONTENT="Jade 1.1/O'Reilly DocBook 3.0 to HTML 4.0"><LINKREV="made"HREF="mailto:online-books@oreilly.com"TITLE="Online Books Comments"><LINKREL="up"HREF="ch03_01.htm"TITLE="3. Dates and Times"><LINKREL="prev"HREF="ch03_11.htm"TITLE="3.10. Short Sleeps"><LINKREL="next"HREF="ch04_01.htm"TITLE="4. Arrays"></HEAD><BODYBGCOLOR="#FFFFFF"><img alt="Book Home" border="0" src="gifs/smbanner.gif" usemap="#banner-map" /><map name="banner-map"><area shape="rect" coords="1,-2,616,66" href="index.htm" alt="Perl Cookbook"><area shape="rect" coords="629,-11,726,25" href="jobjects/fsearch.htm" alt="Search this book" /></map><div class="navbar"><p><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch03_11.htm"TITLE="3.10. Short Sleeps"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 3.10. Short Sleeps"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1"><ACLASS="chapter"REL="up"HREF="ch03_01.htm"TITLE="3. Dates and Times"></A></FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="chapter"HREF="ch04_01.htm"TITLE="4. Arrays"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 4. Arrays"BORDER="0"></A></TD></TR></TABLE></DIV><DIVCLASS="sect1"><H2CLASS="sect1"><ACLASS="title"NAME="ch03-10311">3.11. Program: hopdelta</A></H2><PCLASS="para"><ACLASS="indexterm"NAME="ch03-idx-1000006363-0"></A><ACLASS="indexterm"NAME="ch03-idx-1000006363-1"></A><ACLASS="indexterm"NAME="ch03-idx-1000006363-2"></A>Have you ever wondered why it took so long for someone's mail to get to you? With postal mail, you can't trace how long each intervening post office let your letter gather dust in their back office. But with electronic mail, you can. The message carries in its header <CODECLASS="literal">Received:</CODE><ACLASS="indexterm"NAME="ch03-idx-1000006369-0"></A> lines showing when each intervening mail transport agent along the way got the message.</P><PCLASS="para">The dates in the headers are hard to read. You have to read them backwards, bottom to top. They are written in many varied formats, depending on the whim of each transport agent. Worst of all, each date is written in its own local time zone. It's hard to eyeball <CODECLASS="literal">"Tue,</CODE> <CODECLASS="literal">26</CODE> <CODECLASS="literal">May</CODE> <CODECLASS="literal">1998</CODE> <CODECLASS="literal">23:57:38</CODE> <CODECLASS="literal">-0400"</CODE> and <CODECLASS="literal">"Wed,</CODE> <CODECLASS="literal">27</CODE> <CODECLASS="literal">May</CODE> <CODECLASS="literal">1998</CODE> <CODECLASS="literal">05:04:03</CODE> <CODECLASS="literal">+0100"</CODE> and realize these two dates are only 6 minutes and 25 seconds apart. <CODECLASS="literal"></CODE><ACLASS="indexterm"NAME="ch03-idx-1000006371-0"></A></P><PCLASS="para">The <CODECLASS="literal">ParseDate</CODE><ACLASS="indexterm"NAME="ch03-idx-1000006379-0"></A><ACLASS="indexterm"NAME="ch03-idx-1000006379-1"></A><ACLASS="indexterm"NAME="ch03-idx-1000006379-2"></A> and <CODECLASS="literal">DateCalc</CODE> functions in the Date::Manip module from CPAN can help this:</P><PRECLASS="programlisting">use Date::Manip qw(ParseDate DateCalc);$d1 = ParseDate("Tue, 26 May 1998 23:57:38 -0400");$d2 = ParseDate("Wed, 27 May 1998 05:04:03 +0100");print DateCalc($d1, $d2);<CODECLASS="userinput"><B><CODECLASS="replaceable"><I>+0:0:0:0:0:6:25</I></CODE></B></CODE></PRE><PCLASS="para">That's a nice format for a program to read, but it's still not what the casual reader wants to see. The <EMCLASS="emphasis">hopdelta</EM> program, shown in <ACLASS="xref"HREF="ch03_12.htm#ch03-29485"TITLE="hopdelta">Example 3.1</A>, takes a mailer header and tries to analyze the deltas (difference) between each hop (mail stop). Its output is shown in the local time zone.</P><DIVCLASS="example"><H4CLASS="example"><ACLASS="title"NAME="ch03-29485">Example 3.1: hopdelta</A></H4><PRECLASS="programlisting">#!/usr/bin/perl# hopdelta - feed mail header, produce lines# showing delay at each hop.use strict;use Date::Manip qw (ParseDate UnixDate);# print header; this should really use format/write due to# printf complexitiesprintf "%-20.20s %-20.20s %-20.20s %s\n", "Sender", "Recipient", "Time", "Delta";$/ = ''; # paragraph mode$_ = <>; # read headers/\n\s+/ /g; # join continuation lines# calculate when and where this startedmy($start_from) = /^From.*\@([^\s>]*)/m;my($start_date) = /^Date:\s+(.*)/m;my $then = getdate($start_date);printf "%-20.20s %-20.20s %s\n", 'Start', $start_from, fmtdate($then);my $prevfrom = $start_from;# now process the headers lines from the bottom upfor (reverse split(/\n/)) { my ($delta, $now, $from, $by, $when); next unless /^Received:/; s/\bon (.*?) (id.*)/; $1/s; # qmail header, I think unless (($when) = /;\s+(.*)$/) { # where the date falls warn "bad received line: $_"; next; } ($from) = /from\s+(\S+)/; ($from) = /\((.*?)\)/ unless $from; # some put it here $from =~ s/\)$//; # someone was too greedy ($by) = /by\s+(\S+\.\S+)/; # who sent it on this hop # now random mungings to get their string parsable for ($when) { s/ (for|via) .*$//; s/([+-]\d\d\d\d) \(\S+\)/$1/; s/id \S+;\s*//; } next unless $now = getdate($when); # convert to Epoch $delta = $now - $then; printf "%-20.20s %-20.20s %s ", $from, $by, fmtdate($now); $prevfrom = $by; puttime($delta); $then = $now; }exit;# convert random date strings into Epoch secondssub getdate { my $string = shift; $string =~ s/\s+\(.*\)\s*$//; # remove nonstd tz my $date = ParseDate($string); my $epoch_secs = UnixDate($date,"%s"); return $epoch_secs;}# convert Epoch seconds into a particular date stringsub fmtdate { my $epoch = shift; my($sec,$min,$hour,$mday,$mon,$year) = localtime($epoch); return sprintf "%02d:%02d:%02d %04d/%02d/%02d", $hour, $min, $sec, $year + 1900, $mon + 1, $mday,}# take seconds and print in pleasant-to-read formatsub puttime { my($seconds) = shift; my($days, $hours, $minutes); $days = pull_count($seconds, 24 * 60 * 60); $hours = pull_count($seconds, 60 * 60); $minutes = pull_count($seconds, 60); put_field('s', $seconds); put_field('m', $minutes); put_field('h', $hours); put_field('d', $days); print "\n";}# usage: $count = pull_count(seconds, amount)# remove from seconds the amount quantity, altering caller's version.# return the integral number of those amounts so removed.sub pull_count { my($answer) = int($_[0] / $_[1]); $_[0] -= $answer * $_[1]; return $answer;}# usage: put_field(char, number)# output number field in 3-place decimal format, with trailing char# suppress output unless char is 's' for secondssub put_field { my ($char, $number) = @_; printf " %3d%s", $number, $char if $number || $char eq 's';}</PRE></DIV><PRECLASS="programlisting"><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Sender Recipient Time Delta</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>Start wall.org 09:17:12 1998/05/23</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>wall.org mail.brainstorm.net 09:20:56 1998/05/23 44s 3m</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>mail.brainstorm.net jhereg.perl.com 09:20:58 1998/05/23 2s</I></CODE></B></CODE> <ACLASS="indexterm"NAME="ch03-idx-1000006365-0"></A><ACLASS="indexterm"NAME="ch03-idx-1000006365-1"></A><ACLASS="indexterm"NAME="ch03-idx-1000006365-2"></A><ACLASS="indexterm"NAME="ch03-idx-1000006216-0"></A></PRE></DIV><DIVCLASS="htmlnav"><P></P><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch03_11.htm"TITLE="3.10. Short Sleeps"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 3.10. Short Sleeps"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="book"HREF="index.htm"TITLE="Perl Cookbook"><IMGSRC="../gifs/txthome.gif"ALT="Perl Cookbook"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="chapter"HREF="ch04_01.htm"TITLE="4. Arrays"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 4. Arrays"BORDER="0"></A></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228">3.10. Short Sleeps</TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="index"HREF="index/index.htm"TITLE="Book Index"><IMGSRC="../gifs/index.gif"ALT="Book Index"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228">4. Arrays</TD></TR></TABLE><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><FONTSIZE="-1"></DIV<!-- LIBRARY NAV BAR --> <img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p> <a href="copyrght.htm">Copyright © 2002</a> O'Reilly & Associates. All rights reserved.</font> </p> <map name="library-map"> <area shape="rect" coords="1,0,85,94" href="../index.htm"><area shape="rect" coords="86,1,178,103" href="../lwp/index.htm"><area shape="rect" coords="180,0,265,103" href="../lperl/index.htm"><area shape="rect" coords="267,0,353,105" href="../perlnut/index.htm"><area shape="rect" coords="354,1,446,115" href="../prog/index.htm"><area shape="rect" coords="448,0,526,132" href="../tk/index.htm"><area shape="rect" coords="528,1,615,119" href="../cookbook/index.htm"><area shape="rect" coords="617,0,690,135" href="../pxml/index.htm"></map> </BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -