[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl-Scribes] Basic Perl Filters in BBEdit (much revised)



I've taken some of David Steffan's additions and worked them in, making 
allowances for the inclusion of Jim's quote.  I've moved the discussion of 
switches to a more appropriate location given the added material.  More 
references, a short list of thank-yous, some more formatting changes, 
munging the perl examples so they no longer generate HTML syntax errors 
(earlier versions display correctly, but don't validate as HTML 4.0). Other 
little bits of minor tweaking.

We need to rip through this one more time before sending it off to Baiju.

My Mac is still flaky, and still under Mac OS 7.5.5 until the memory 
upgrade arrives (they didn't ship it until late yesterday, and there's some 
question in my mind over whether they've sent it to the right place).  I'm 
managing without most of my valued extensions. Woe betides if I crash 
today, for I may not be able to successfully reboot.

--B

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
   "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
 <title>Perl Filters in BBEdit</title>
 <meta name="generator" content="BBEdit 5.1.1">
</head>
<body bgcolor="#ffffff">
<h2>Basic Perl Filters in BBEdit</h2>

<p>
Back in issue #7, Vicki Brown wrote about her &quot;<a 
href="http://www.perlmonth.com/columns/mac_perl/mac_perl.html?issue=7">MacPe
rl Development Environment</a>&quot;. For the most part, what she describes 
there is a typical setup for a serious MacPerler. In her article, Vicki 
covered, among other things, BBEdit, from <a 
href="http://www.barebones.com/">Bare Bones Software</a>. As of version 
5.1, BBEdit has MacPerl support built in. Older versions used a plug-in 
module for roughly the same functionality. However having built-in support 
means MORE than just the ability to use BBEdit as a text-editor and front 
end for MacPerl.
</p>

<p>
BBEdit allows you to create &quot;Perl Filters&quot;: scripts to which a 
text selection, or even an entire document may be passed, which then return 
their results to BBEdit. BBEdit isn't the only Mac text-editor with 
built-in Perl support, and neither is it the only such editor which lets 
you use Perl scripts as text filters.  <a 
href="http://alpha.olm.net/">Alpha</a> has similar functionality and a very 
vocal following.  I'm hoping to corral one of those vocal followers into 
writing a column on the subject.
</p>

<p>
I spoke (via email) with Jim Correia, Senior Software Engineer at Bare 
Bones Software.  Although perl filters weren't his idea (credit rightly 
goes to Brad Hanson for that), he did do a complete rewrite of the code for 
BBEdit 5.1.  It's safe to say that Jim's intimately familiar with the code. 
 I asked Jim to describe what a Perl Filter does:
</p>

<blockquote>
A perl filter is a perl program that transforms the selection in an active 
BBEdit window.  The program takes input as a file path on the command line, 
and writes its output to STDOUT.  If we were on a unix-like operating 
system, the program might be invoked liked this
</blockquote>

<blockquote>
<code>perl filter.plx /usr/tmp/bbedit_selection.txt</code>
</blockquote>

<blockquote>
On the Macintosh, there isn't a command line.  Instead BBEdit writes out 
the selection to a temporary file called [DocumentName].&sect; then sends 
an apple event to the MacPerl application giving it the program to execute, 
the command line parameters (the data file) and sets up some flags telling 
MacPerl it should return STDOUT and STDERR in the apple event reply.  
BBEdit replaces the selection in the active window with the contents of 
STDOUT.
</blockquote>

<blockquote>
So for instance, if I had a window opened called &quot;Testing Perl 
Filters&quot; and ran the following perl filter:
</blockquote>

<blockquote>
<pre>
    #!perl -w
    use strict;

    print "$ARGV[0]\n";
</pre>
</blockquote>

<blockquote>
the selection would be replaced with
</blockquote>

<blockquote>
<pre>
    Boot:Temporary Items:Testing Perl Filters.&sect;
</pre>
</blockquote>

<blockquote>
More interesting is when we actually have some useful transformation we 
want to perform on the data.  One such example would be sorting the lines 
in a data file:
</blockquote>

<blockquote>
<pre>
    #!perl -w
    use strict;

    my @lines = &lt;&gt;;
    @lines = sort @lines;
    foreach(@lines){
        print;
    }
</pre>
</blockquote>

<blockquote>
You can use all of the command line flags in a perl filter that MacPerl 
would normally understand.  Taking advantage of this, a filter which 
reverses all of the characters in each line becomes the canonical perl one 
liner:
</blockquote>

<blockquote>
<pre>
    #!perl -wpl
    $_ = reverse;
</pre>
</blockquote>

<h3>TMTOWTDI</h3>

<p>
I've seen more than my share of Perl Filters, and a lot of evidence that 
MacPerler's in general are unaware of which #! switches work under MacPerl, 
and when and how to use them.  This is really a topic for an entirely 
separate column, but I'll try to deal with it briefly here.  Most switches 
work with MacPerl.  Of those which might be considered most useful in the 
context of Perl Filters:
</p>

<pre>
-n (iterate entire script using &lt;&gt;) works as expected
-p (iterate entire script using &lt;&gt; printing each line) works as 
expected
-l (automatically chomp line endings from input 
   -- with -n or -p no terminating \n needed on output) works as expected
-e (execute following string as command-line) generates error message 
   -- not emulated.  Works only under MPW Perl.
-0 (sets $\ in octal) works as expected
</pre>

<p>
Although in this context, I can't imagine you doing such a thing, I feel 
the need to point out that:
</p>

<p>
-u (does core dump) Generates error message: &quot;Believe me, you don't 
want to use &quot;-u&quot; on a Macintosh.&quot;
</p>

<p>
In the course or preparing this article, Jim Correia first proposed the 
&quot;reverse lines&quot; filter as follows.
</p>

<pre>
#!perl -w
# Here is quickie example that reverses the selected lines.  If it looks
# like C it is because I write C all day long and it influences my perl
# (of which I don't do or know too much):

use strict;

while ( &lt;&gt; )
{
     my $s = $_;
 
     chomp($s);
 
     my $ct = length($s);
 
     for(my $i=$ct-1; $i&gt;=0; $i--)
     {
         print substr($s, $i, 1);
     }

     print "\n";
}
</pre>

<p>
If you like C, Perl lets you program like a C programmer.  If you like AWK, 
Perl lets you program like an AWK programmer, but if Perl is your native 
language, you can take advantage of Perl's advanced string handling and 
command line switches to do the same thing in one short line. 
</p>

<p>
First, note that the entire script is encased in a <code>while()</code>. 
This calls for either <code>-p</code> or <code>-n</code> on the hash-bang 
to remove that construction.  Second, note that the <code>while()</code> 
ends with a <code>print()</code>.  This makes it <code>-p</code> without a 
doubt. 
Third, note that the script <code>chomp()</code>s input and appends 
&quot;\n&quot; to its output. Assigning  <code>$_</code> to <code>$s</code> 
is an uncessary step. It's more idiomatically perlish to just 
&quot;<code>chomp;</code>&quot;, which performs <code>chomp($_)</code> by 
default, thus dropping <code>$s</code> from the namespace.  This calls for 
<code>-l</code>, thus removing both <code>chomp()</code> and 
<code>print()</code>.
</p>

<p>
Since the purpose of the <code>for()</code> loop is duplicated by the 
built-in function <code>reverse()</code>, and reverse applies to 
<code>$_</code> by default, the entire for loop becomes &quot;<code>$_ = 
reverse</code>&quot;, and all the associated variables are dropped from the 
namespace. The idiomatic version is <b>trivially</b> faster and less memory 
intensive. In practice, both versions are too fast to time.
</p>

<h3>A Useful One-Line Filter</h3>

<p>
For the most part, Perl Filters tend to be exactly those sort of short (one 
or two line) scripts, which do various regular expression or other 
functions not possible with BBEdit's own regex capability.  Frequently, you 
need to make multiple passes over a document, or parse it in a way which 
isn't possible with regex alone, like so:
</p>

<pre>
#!perl -p 
s/(\d+)-(\d+)/$1 &gt; $2 ? "$1-$2" : join ', ', $1 .. $2/ge
</pre>

<p>
Alas, I'm not the author of this gem.  This came about as the result of one 
of the frequent filter-generation sessions on the BBEdit-Talk[<a 
href="#talk-list">1</a>] list.  On a Friday, someone blithely asked for a 
script which could parse a certain set of strictly formatted strings of 
numbers.  The problem was first approached using a rather convoluted 
AppleScript, which even the author concluded was ugly.  Next, a Perler 
tried his hand.  Finally, Ronald Kimball stepped in, reducing the Perl 
script to its most idiomatically concise form.  Ron claims the result is 
again &quot;ugly&quot;, but the original poster referred to it as 
&quot;inspirational&quot;.
</p>

<p>
Oh, and what does it do?  The answer is <a 
href="http://search.barebones.com/action.lasso?-database=lists.fp3&amp;-
layout=import&amp;-response=%2ftalk%5flists%2fdetail.html&amp;-
recid=47440&amp;-search">here</a>.
</p>

<h3>Filters using Perl Modules</h3>

<p>
Are you lazy?  You just don't want to open your mail client to post mail?  
You'd rather stay in your text-editor?  Well, so did Rory Campbell-Lange:
</p>

<pre>
#!perl -w
# campbell-lange@easynet.co.uk (Rory Campbell-Lange)
# only barely modified from the example in
# the Mail::Send POD

require Mail::Send;

@allmail = &lt;&gt;;
$to = shift @allmail;
chomp $to;
$subject = shift @allmail;
chomp $subject;
$msg = new Mail::Send;

$msg-&gt;to($to);
$msg-&gt;subject($subject);

$fh = $msg-&gt;open;
print $fh "@allmail";
$fh-&gt;close;
</pre>

<p>
Oh, but that doesn't handle MIME attachments you say?
</p>

<pre>
#!perl -w
# campbell-lange@easynet.co.uk (Rory Campbell-Lange)
# only barely modified from the example in
# the MIME::Lite POD

use MIME::Lite;
MIME::Lite-&gt;send('smtp', "mymail.example.org", Timeout=&gt;200);

    my $to = shift;
    my $sub = shift;
    my $filetoattach = shift;
    my $filename = shift;
    my @message = @_;

  $msg = new MIME::Lite
    From     =&gt;'me@mymail.co.uk',
    To       =&gt; $to,
    #Cc      =&gt; couldbeshift,
    Subject  =&gt; $sub,
    Type     =&gt;'TEXT',
    Data     =&gt; @message,
    
  attach $msg
    Type     =&gt;'image/gif',
    Path     =&gt; $filetoattch,

    Encoding =&gt; 'base64',
    Filename =&gt; $filename;

$msg-&gt;send;
</pre>

<p>
A sufficiently intrepid programmer could easily use this as the starting 
point for a complete mail client, written as a BBEdit Perl Filter!
</p>

<h3>Filters with No Input</h3>

<p>
Nothing says that a "filter" has to take an input to produce an output.  
This example creates a timestamp:
</p>

<pre>
#!perl
 my ( $year, $month, $day, $hour, $min, $sec) = reverse((localtime)[0 .. 
5]);
 printf('%04d:%02d:%02d  
%02d:%02d:%02d',$year+1900,$month+1,$day,$hour,$min,$sec);
</pre>

<h3>Next Issue</h3>

<p>
I've done other scripts which do more complex things, such as generating an 
XML file, then parsing it to create HTML[<a href="#mother-of-perl">2</a>].  
However, I'm saving the really tricky Perl Filters for Chris Nandor.  Next 
Month Chris will show us "Perl Filter Tricks".  Stay tuned.
</p>

<h3>References/Footnotes</h3>

<p>
<a name="talk-list">1) BBEdit-Talk Mailing List:</a> &lt;<a 
href="mailto:bbedit-talk@barebones.com">bbedit-talk@barebones.com</a>&gt; 
To subscribe, visit the <a href="http://www.barebones.com/">Bare Bones 
Software</a> web site.
</p>

<p>
<a name="mother-of-perl">2) Mother of Perl</a> <a 
href="http://webreference.com/perl/scripts/">http://webreference.com/perl/sc
ripts/</a>, which is actually <b>rewritten</b> as a BBEdit Include file.  
Most of the code is Jonathan Eisenzopf's not mine, I merely had to figure 
out new ways of passing data to it.
</p>

<h3>Thanks To:</h3>

<p>
Larry Moore and David Steffan for taking my partly finished text, and some 
hastily-written notes and adding immeasurably to this article, and to Rich 
Morin for his nit-picky copy-editing. Thanks to everyone else on 
MacPerl-Scribes as well, for being such good sports when my computer was 
down, and keeping progress on the aritcle going. Even where I haven't taken 
all the advice given, I did pay attention. Additional thanks to Jim Correia 
for taking time from his busy schedule to add his comments as well.
</p>

</body>
</html>



==== Want to unsubscribe from this list?
==== Send mail with body "unsubscribe" to macperl-scribes-request@macperl.org