View Source:
PerlFastLane
Note:
This page has been locked and cannot be edited.
!!!Perl In the Fast -lane Ok, ok, sorry for the terrible title. But hey I have exciting news: I think I've finally come to understand the awesomeness of the <code>perl -lane</code> method of running one line perl command scripts. Unfortunately, this does mean I'm saying goodbye to awk. I'm sorry old buddy it's been a long run but it's time for me to move on. Anyway, in my real job I often have to run scripts on thousands of remote systems via a parallel ssh execution tool (the details of which are unimportant here). For example, I might need to confirm that the version of a particular configuration file is consistent across all the hosts. I can use my parallel execution tool to easily run a command on all 10,000 or so hosts and dump the results in a text file. The problem is I end up with a very long result file that looks like this: <verbatim> >>> argle.example.com command: /usr/sbin/db_update -check db_update: Version available from dist: 1.15.130 (built: Mon Jan 31 02:32:12 2011) db_update: Installed database version: 1.15.130 (built: Mon Jan 31 02:32:12 2011) db_update: Installed database status: OK (Matches dist version) db_update: Installed database age: 30 days since db was built >>> bargle.example.com command: /usr/sbin/db_update -check db_update: Version available from dist: 1.15.130 (built: Mon Jan 31 02:32:12 2011) db_update: Installed database version: 1.15.130 (built: Mon Jan 31 02:32:12 2011) db_update: Installed database status: OK (Matches dist version) db_update: Installed database age: 30 days since db was built </verbatim> and so on for many pages. The annoyance with this is I really only am interested in the actual 'Installed database version'. However if I grep out just those lines, I then lose the context of which host the result came from. Something more clever is needed. In the past I've done this with the usual command line tools like grep and awk. for example, I might run something like this as the command on each host: <verbatim> echo -n "$(hostname): " && /usr/sbin/db_update -check | grep "Installed database version" | awk '{ print $5 }' </verbatim> or, a slight improvement I might use is to make awk do the work of grep too, and save a process: <verbatim> echo -n "$(hostname): " && /usr/sbin/db_update -check | awk '/Installed database version/ { print $5 }' </verbatim> which works just fine for sure, but both versions seem a little ugly, what with that awkward *echo* at the beginning to get the hostname. I finally decided to see if I could do something more clever with a [perl one liner|http://www.catonmat.net/blog/perl-one-liners-explained-part-one] instead. I've always liked the idea of [one line perl scripts|http://ajs.com/ajswiki/Perl_one-liners] but the different command-line arguments always tripped me up. How do you remember to use <code>perl -ne</code> vs. <code>perl -pe</code> for example? Also, here's a big sticking point for me - how do you replace the smart line splitting functionality in awk, which allows me to do things like <code>awk '{ print $5}'</code> in the above example? I have to credit the [Ksplice blog|http://blog.ksplice.com/2010/05/top-10-perl-one-liner-tricks/] for finally making me understand *perl autosplit mode*. I learned that all I needed to do was run my command with <code>perl -a</code> to make perl split every line into the array <code>@F</code> which I could then use in exactly the same way I was used to dealing with awk positional parameters <code>$1</code>, <code>$2</code>, etc. Then, it's a matter of selecting other perl command line arguments to get the results I desired. First of all of course <code>-e</code> is mandatory in all cases because that's what tells perl to read the script from the command line. Then I knew I wanted to run this thing in a loop, so I should use <code>-n</code> or <code>-p</code> to save myself having to implement the loop in the script. <code>-p</code> prints the output every time. I don't want to do that so let's use <code>-n</code> to just do the loop. I knew I wanted to use the perl autosplit mode to make this script work similarly to awk, so that's <code>-a</code>. Rounding it all out is the hard to understand <code>-l</code> option. The explanation of <code>-l</code> is complicated, but you can ignore that and just read the 'record separators' section of [this page|http://www.perl.com/pub/2004/08/09/commandline.html]. All you need to remember about <code>-l</code> is it automatically chomps the newline character off every input line, and puts it back on the output. This allows you to use <code>print</code> in your one liner and not worry about missing newlines in the output. Putting those command line options together, I wrote this script: <verbatim> /usr/sbin/db_update -check | perl -MSys::Hostname -lane 'print hostname.": ".$F[4] if /Installed database version/' </verbatim> A final note about how this script is constructed: I know I could have used the backtick operator to obtain the system hostname in my one liner. However, since I was going all perl here it seemed best to figure out how to obtain the hostname directly in perl. Since ##~Sys::~Hostname## automatically exports the <code>hostname</code> finction, all I had to do was load the module via the <code>-M</code> command line option, and call <code>hostname</code> directly in my script. When I run that script against my input file the result is this: <verbatim> argle.example.com: 1.15.130 bargle.example.com: 1.15.130 boo.example.com: 1.15.49 </verbatim> which is exactly what I wanted. The key idea here is to use <code>-a</code> to autosplit the input line into array <code>@F</code>. You can then use <code>$F~[4]</code> exactly like you do in <code>awk '{ print $4 }'</code>. This whole thing is generally a replacement for the standard awk 'find a line and output part of it' idiom: <code>awk '/Installed database version/ '{ print $4 }'</code>. The advantage here is the integration of additional data (like the hostname) is quite simple. I realize that awk programmers out there can probably do this in an equally simple way. If so, please give me your solution in the comments! So there you go - it can pay to take some time to figure out how to use tools like perl one liners. It's easy to get stuck in a rut of __grep and awk__ or whatever. I think it's always important to be trying out new tools and expanding your skillset. For me this meant finally committing to perl one liners. ps - as a result of running this I found that 71 of 9565 hosts has an old version of the database as a result of my cron-based autoupdater, while the rest had the current version. That's a pretty good result when you're dealing with almost 10,000 hosts! CategoryGeekStuff CategoryPerl CategoryBlog
Please enable JavaScript to view the
comments powered by Disqus.
HollenbackDotNet
Home Page
Popular Pages
All Categories
Main Categories
General Interest
Geek Stuff
DevOps
Linux Stuff
Pictures
Search
Toolbox
RecentChanges
RecentNewPages
What links here
Printable version
AllPages
RecentChanges
Recent Changes Cached
No changes found
Favorite Categories
ActionPage
(150)
WikiPlugin
(149)
GeekStuff
(137)
PhpWikiAdministration
(102)
Help/PageList
(75)
Help/MagicPhpWikiURLs
(75)
Blog
(69)
Pictures
(60)
GeneralInterest
(44)
LinuxStuff
(38)
Views
View Page
View Source
History
Diff
Sign In