The top 10 tricks of Perl one-liners

Posted in System administration on May 27th, 2010 by Nelson Elhage40 Comments

I’m a recovering perl hacker. Perl used to be far and away my language of choice, but these days I’m more likely to write new code in Python, largely because far more of my friends and coworkers are comfortable with it.

I’ll never give up perl for quick one-liners on the command-line or in one-off scripts for munging text, though. Anything that lasts long enough to make it into git somewhere usually gets rewritten in Python, but nothing beats perl for interactive messing with text.

Perl, never afraid of obscure shorthands, has accrued an impressive number of features that help with this use case. I’d like to share some of my favorites that you might not have heard of.

One-liners primer

We’ll start with a brief refresher on the basics of perl one-liners before we begin. The core of any perl one-liner is the -e switch, which lets you pass a snippet of code on the command-line:
perl -e 'print "hi\n"' prints “hi” to the console.

The second standard trick to perl one-liners are the -n and -p flags. Both of these make perl put an implicit loop around your program, running it once for each line of input, with the line in the $_ variable. -p also adds an implicit print at the end of each iteration.

Both of these use perl’s special “ARGV” magic file handle internally. What this means is that if there are any files listed on the command-line after your -e, perl will loop over the contents of the files, one at a time. If there aren’t any, it will fall back to looping over standard input.

perl -ne 'print if /foo/' acts a lot like grep foo, and perl -pe 's/foo/bar/' replaces foo with bar

Most of the rest of these tricks assume you’re using either -n or -p, so I won’t mention it every time.

The top 10 one-liner tricks

Trick #1: -l

Smart newline processing. Normally, perl hands you entire lines, including a trailing newline. With -l, it will strip the trailing newline off of any lines read, and automatically add a newline to anything you print (including via -p).

Suppose I wanted to strip trailing whitespace from a file. I might naïvely try something like

  perl -pe 's/\s*$//'

The problem, however, is that the line ends with "\n", which is whitespace, and so that snippet will also remove all newlines from my file! -l solves the problem, by pulling off the newline before handing my script the line, and then tacking a new one on afterwards:

  perl -lpe 's/\s*$//'

Trick #2: -0
Occasionally, it’s useful to run a script over an entire file, or over larger chunks at once. -0 makes -n and -p feed you chunks split on NULL bytes instead of newlines. This is often useful for, e.g. processing the output of find -print0. Furthermore, perl -0777 makes perl not do any splitting, and pass entire files to your script in $_.

  find . -name '*~' -print0 | perl -0ne unlink

Could be used to delete all ~-files in a directory tree, without having to remember how xargs works.

Trick #3: -i
-i tells perl to operate on files in-place. If you use -n or -p with -i, and you pass perl filenames on the command-line, perl will run your script on those files, and then replace their contents with the output. -i optionally accepts an backup suffix as argument; Perl will write backup copies of edited files to names with that suffix added.

  perl -i.bak -ne 'print unless /^#/' script.sh

Would strip all whole-line commands from script.sh, but leave a copy of the original in script.sh.bak.

Trick #4: The .. operator
Perl’s .. operator is a stateful operator — it remembers state between evaluations. As long as its left operand is false, it returns false; Once the left hand returns true, it starts evaluating the right-hand operand until that becomes true, at which point, on the next iteration it resets to false and starts testing the other operand again.

What does that mean in practice? It’s a range operator: It can be easily used to act on a range of lines in a file. For instance, I can extract all GPG public keys from a file using:

     perl -ne 'print if /-----BEGIN PGP PUBLIC KEY BLOCK-----/../-----END PGP PUBLIC KEY BLOCK-----/' FILE
Trick #5: -a

-a turns on autosplit mode – perl will automatically split input lines on whitespace into the @F array. If you ever run into any advice that accidentally escaped from 1980 telling you to use awk because it automatically splits lines into fields, this is how you use perl to do the same thing without learning another, even worse, language.

As an example, you could print a list of files along with their link counts using

ls -l | perl -lane 'print "$F[7] $F[1]"'

Trick #6: -F
-F is used in conjunction with -a, to choose the delimiter on which to split lines. To print every user in /etc/passwd (which is colon-separated with the user in the first column), we could do:

perl -F: -lane 'print $F[0]' /etc/passwd
Trick #7: \K
\K is undoubtedly my favorite little-known-feature of Perl regular expressions. If \K appears in a regex, it causes the regex matcher to drop everything before that point from the internal record of “Which string did this regex match?”. This is most useful in conjunction with s///, where it gives you a simple way to match a long expression, but only replace a suffix of it.

Suppose I want to replace the From: field in an email. We could write something like

perl -lape 's/(^From:).*/$1 Nelson Elhage <nelhage\@ksplice.com>/'

But having to parenthesize the right bit and include the $1 is annoying and error-prone. We can simplify the regex by using \K to tell perl we won’t want to replace the start of the match:

perl -lape 's/^From:\K.*/ Nelson Elhage <nelhage\@ksplice.com>/'
Trick #8: $ENV{}
When you’re writing a one-liner using -e in the shell, you generally want to quote it with ‘, so that dollar signs inside the one-liner aren’t expanded by the shell. But that makes it annoying to use a ‘ inside your one-liner, since you can’t escape a single quote inside of single quotes, in the shell.

Let’s suppose we wanted to print the username of anyone in /etc/passwd whose name included an apostrophe. One option would be to use a standard shell-quoting trick to include the ‘:

perl -F: -lane 'print $F[0] if $F[4] =~ /'"'"'/' /etc/passwd

But counting apostrophes and backslashes gets old fast. A better option, in my opinion, is to use the environment to pass the regex into perl, which lets you dodge a layer of parsing entirely:

env re="'" perl -F: -lane 'print $F[0] if $F[4] =~ /$ENV{re}/' /etc/passwd

We use the env command to place the regex in a variable called re, which we can then refer to from the perl script through the %ENV hash. This way is slightly longer, but I find the savings in counting backslashes or quotes to be worth it, especially if you need to end up embedding strings with more than a single metacharacter.

Trick #9: BEGIN and END
BEGIN { ... } and END { ... } let you put code that gets run entirely before or after the loop over the lines.

For example, I could sum the values in the second column of a CSV file using:

perl -F, -lane '$t += $F[1]; END { print $t }'
Trick #10: -MRegexp::Common
Using -M on the command line tells perl to load the given module before running your code. There are thousands of modules available on CPAN, numerous of them potentially useful in one-liners, but one of my favorite for one-liner use is Regexp::Common, which, as its name suggests, contains regular expressions to match numerous commonly-used pieces of data.

The full set of regexes available in Regexp::Common is available in its documentation, but here’s an example of where I might use it:

Neither the ifconfig nor the ip tool that is supposed to replace it provide, as far as I know, an easy way of extracting information for use by scripts. The ifdata program provides such an interface, but isn’t installed everywhere. Using perl and Regexp::Common, however, we can do a pretty decent job of extracing an IP from ip‘s output:

ip address list eth0 | \
  perl -MRegexp::Common -lne 'print $1 if /($RE{net}{IPv4})/'

So, those are my favorite tricks, but I always love learning more. What tricks have you found or invented for messing with perl on the command-line? What’s the most egregious perl “one-liner” you’ve wielded, continuing to tack on statements well after the point where you should have dropped your code into a real script?


perl -lne s/rebooting//i

Tired of babysitting your systems while you reboot to install updates? Eliminate reboots with Ksplice Uptrack, and spend that time honing your perl scripts instead!

Share :
  • Twitter
  • Reddit
  • Digg
  • Facebook
  • del.icio.us
  • StumbleUpon
  1. Justin says:

    #8 – I use chr(39) a lot. And if you need to single-quoted string, there’s q{…}.

  2. Chas. Owens says:

    If you use the Eskimo kiss operator }{ you can avoid END blocks:

    perl -lne '$h{$_}++ }{ print "$_ => $h{$_}" for sort keys %h' FILE
    

    After all, what is a little code injection attack between friends?

  3. Chas. Owens says:

    Also, if you use Regexp::Common all of the time, you can include it in PERL5OPT so it always get’s used:

    export PERL5OPT=Regexp::Common
    perl -E 'say $RE{net}{IPv4}'
    
  4. Daniel Lyons says:

    You can also accomplish #2 with:

    find /some/path -name ‘*~’ -delete

  5. You can also do #2 with

    find /some/path -name '*~' -exec rm '{}' \;
    

    …which would allow you do do the dangerous:

    # delete all files with "suffix" as a suffix even if they are a directory
    # (and if they are a directory delete all children files)
    find /some/path -name '*suffix' -exec rm -Rf '{}' \;
    

    or update the timestamp on the files

    find /some/path -name '*suffix' -exec touch '{}' \;
    
  6. \K is great, thanks.

    As far as giving up awk, the one feature I find myself using in awk is ‘/some.pattern/ {print $something}’. Should I just use perl -nle ‘print $something if /some.pattern/’? Is there a way to use perl -ple with conditionals, especially with multiple blocks — something like perl -pale ‘$1 if /foo/; $2 if /bar/’?

  7. Very nice! I wasn’t aware of \K, that’s just brilliant. Also, ditto Chas on the “Eskimo Kiss” operator (although I’ve never heard it called that before): very useful idiom.

  8. Nelson Elhage says:

    Chas: Nifty, I hadn’t seen the eskimo kiss operator. That’s slightly evil, but clever.

    The PERL5OPT tip is a good one, too, although I tend to dislike putting things in PERL5OPT for fear I’ll forget to unset it and it will subtly change the behavior of perl scripts I run somehow. It’s probably safe to do something like loading Regexp::Common, though. (Also, I think you have a slight typo — I believe you want PERL5OPT=-MRegexpCommon)

  9. Nelson Elhage says:

    Daniel and Terence: Yeah, I’m aware of -delete and -exec, but felt it made a decent concise example of perl -0. Why learn more tools than you need to, if perl does the trick?

    Also, you want to avoid -exec for something like this, if you can. Spawning a new child for every file you want to process is slow and inefficient. The rm -rf trick would be better written:

    find /some/path -name '*~' -print0 | xargs -0 'rm -rf'
    

    which will feed files or directories to rm processes in chunks.

  10. Herbie says:

    Frankly, in my opinion, Perl is slightly less understandable than
    double Klingon with a heavy Martian dialect. It seems that
    afficionados take pleasure in writing incomprehensible code that
    does esoteric things. I haven’t tried Python yet but if Perl
    programmers like it I imagine it is equally awful. Sorry if I offend
    but I prefer coding in languages that are human readable.

  11. Josh says:

    My most egregious perl failing is the reverse. I’ve often found myself typeing perl -i -pe ‘….’, and I I love that functionality, but I have also found myself putting in a script:
    system “perl -i -pe ‘…’”

    … and that’s just wrong. I can’t find a shorter way to do it, though.

  12. Josh: take a look at this: http://perl.plover.com/TieFile/BLURB.html. The Tie::File module might be just what you wanted! I personally just use File::Slurp… for example:

    perl -MFile::Slurp -e '$file = shift; write_file "$file.new", map { s/[\$@%&*]//g; $_ } read_file $file' some_perl_script.pl
    

    Will strip out all the sigils (and lots more!) from some_perl_script.pl and write the changed version to some_perl_script.pl.new.

    You could also write a subroutine that accepts a closure which gets executed in place of the map.

    Also, if you need atomic file modifications, there’s File::AtomicWrite.

  13. Shawn H Corey says:

    My favourite Perl one-liner, the Perl Calculator:

    perl -ple'$_=eval'
    

    Type `exit` to quit.

    This simply evaluates the string you type and prints out the answer. Example:

    $perl -ple'$_=eval'
    atan2(0,-1)
    3.14159265358979
    exit
    $
    
  14. Nelson Elhage says:

    Shawn: Cute. I assume you’re also familiar with the perl -de1 trick, which drops you into the perl debugger, which is also a passable perl REPL.

    Of course, if you do this more than occasionally, I strongly recommend installing Devel::REPL, which provides a real, industrial-strength, perl REPL with readline, Data::Dump::Streamer pretty-printing, and more.

  15. Also, you want to avoid -exec for something like this, if you can. Spawning a new child for every file you want to process is slow and inefficient.

    That’s why you should use find foo -exec bar '{}' \+ (plus at the end instead of semicolon), which only forks off a single process. And if you start running into command line length limitations (which you would also run into with perl), then you switch over to -print0 | xargs -0.

    Why learn more tools than you need to, if perl does the trick?

    Why learn perl when the tools you already know do the trick</snark>

  16. Tom Mathews says:

    This is not a one-liner, but I like to combine -e with -d, the debugger, for a quick calculator:

    $ perl -d -e ""
    
    Loading DB routines from perl5db.pl version 1.3
    Editor support available.
    
    Enter h or `h h' for help, or `man perldebug' for more help.
    
    Debugged program terminated.  Use q to quit or R to restart,
      use o inhibit_exit to avoid stopping after program termination,
      h q, h R or h o to get additional info.
      DB p 60*60*24
    86400
      DB q
    $
    
  17. Marko Loparic says:

    Great post! I didn’t know about \K and the .. operator thanks a lot!

    I also do now in Python everything that takes more than one line. I love Python and first I thought I would forget Perl completely. But I still use it quite often in the command line and it is really unbeatable, specially when combined with unix commands.

    Marko

  18. fedov says:

    If you have Perl 5.10 or greater you should always use -E. It activates all new features and gives you for example “say” (print with newline) and smart matching “~~” which are really useful.

  19. Jay says:

    Adam: \+ at the end of find? Yeah, learn GNU find extensions and rely on them instead of using Perl? Good strategy.

  20. Grant McLean says:

    Geoffrey Thomas: for awk-like behaviour you can use the && operator to combine a match and an action. I often use it like this:

    perl -ne ‘/Name: (\w+)/ && print $1′

    Note in this case $1 is the captured bit from the regex – not the awk-style field #1.

  1. [...] Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog By zungr00ando Check this out: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  2. [...] Posted by mewexflipr under Uncategorized Leave a Comment  Found this today: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  3. [...] and software blog May 28, 2010, 12:10 am Filed under: Uncategorized Check this out: The top 10 tricks of Perl one-liners – System administration and software blog Leave a Comment Leave a Comment so far Leave a comment RSS feed for comments on this post. [...]

  4. [...] Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog I’ll never give up perl for quick one-liners on the command-line or in one-off scripts for munging text, though. Anything that lasts long enough to make it into git somewhere usually gets rewritten in Python, but nothing beats perl for interactive messing with text. (tags: perl shell line one) [...]

  5. [...] Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog 27 May 2010 | Uncategorized | Trackback | del.icio.us | Stumble it! | View Count : 0 Next Post [...]

  6. [...] Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog Cool link: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  7. [...] The top 10 tricks of Perl one-liners – System administration and software blog By zunkirm Check this out: The top 10 tricks of Perl one-liners – System administration and software blog [...]

  8. [...] May 28, 2010 · Leave a Comment Another good one: The top 10 tricks of Perl one-liners – System administration and software blog [...]

  9. [...] May 28, 2010 · Leave a Comment Found this today: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  10. [...] and software blog May 28, 2010, 9:00 am Filed under: Uncategorized Found this today: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog Leave a Comment Leave a Comment so far Leave a comment RSS feed for comments on this post. [...]

  11. [...] Ksplice » The top 10 tricks of Perl one-liners – System … [...]

  12. [...] The top 10 tricks of Perl one-liners By momanvioundu Another good one: The top 10 tricks of Perl one-liners [...]

  13. [...] Los 10 mejores trucos de Perl one-liners Posted By: pbruna  Published in linux 29May Excelente recopilación en: http://blog.ksplice.com/2010/05/top-10-perl-one-liner-tricks/ [...]

  14. [...] May 30, 2010 · Leave a Comment Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  15. [...] Comment! Cool link: Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog [...]

  16. [...] Perl one-liners Filed under: Linux — 0ddn1x @ 2010-06-01 20:44:45 +0000 http://blog.ksplice.com/2010/05/top-10-perl-one-liner-tricks/ Leave a Comment TrackBack [...]

  17. [...] Ksplice » The top 10 tricks of Perl one-liners – System administration and software blog (tags: bash cli perl programming reference regex scripting tricks tips unix linux) [...]

  18. [...] and prints them out to the console. And if you misspell a word it will offer some suggestions…The top 10 tricks of Perl one-liners.Un numéro spécial de Nature sur la mesure de la science (scientométrie/bibliométrie).“Un [...]

  19. [...] perlbuzz trobo aquest interessant recull de 10 trucs a tenir en compte alhora d’escriure línia accions/scripts Perl d’una sola línia. [...]

  20. [...] The top 10 tricks of Perl one-liners – a blog entry from ksplice.com that goes through some practical perl one-liner shorthands. [...]

Leave a Reply