How can I convert Perl one-liners into complete sc

2019-01-31 19:16发布

I find a lot of Perl one-liners online. Sometimes I want to convert these one-liners into a script, because otherwise I'll forget the syntax of the one-liner.

For example, I'm using the following command (from nagios.com):

tail -f /var/log/nagios/nagios.log | perl -pe 's/(\d+)/localtime($1)/e'

I'd to replace it with something like this:

tail -f /var/log/nagios/nagios.log | ~/bin/nagiostime.pl

However, I can't figure out the best way to quickly throw this stuff into a script. Does anyone have a quick way to throw these one-liners into a Bash or Perl script?

6条回答
闹够了就滚
2楼-- · 2019-01-31 19:31

You can convert any Perl one-liner into a full script by passing it through the B::Deparse compiler backend that generates Perl source code:

perl -MO=Deparse -pe 's/(\d+)/localtime($1)/e'

outputs:

LINE: while (defined($_ = <ARGV>)) {
    s/(\d+)/localtime($1);/e;
}
continue {
    print $_;
}

The advantage of this approach over decoding the command line flags manually is that this is exactly the way Perl interprets your script, so there is no guesswork. B::Deparse is a core module, so there is nothing to install.

查看更多
Evening l夕情丶
3楼-- · 2019-01-31 19:31

Take a look at perlrun:

-p

causes Perl to assume the following loop around your program, which makes it iterate over filename arguments somewhat like sed:

LINE:
while (<>) {
    ... # your program goes here
} continue {
    print or die "-p destination: $!\n";
}

If a file named by an argument cannot be opened for some reason, Perl warns you about it, and moves on to the next file. Note that the lines are printed automatically. An error occurring during printing is treated as fatal. To suppress printing use the -n switch. A -p overrides a -n switch.

BEGIN and END blocks may be used to capture control before or after the implicit loop, just as in awk.

So, simply take this chunk of code, insertyour code at the "# your program goes here" line, and viola, your script is ready!

Thus, it would be:

#!/usr/bin/perl -w
use strict; # or use 5.012 if you've got newer perls

while (<>) {
    s/(\d+)/localtime($1)/e
} continue {
    print or die "-p destination: $!\n";
}
查看更多
可以哭但决不认输i
4楼-- · 2019-01-31 19:31

Robert has the "real" answer above, but it's not very practical. The -p switch does a bit of magic, and other options have even more magic (e.g. check out the logic behind the -i flag). In practice, I'd simply just make a bash alias/function to wrap around the oneliner, rather than convert it to a script.

Alternatively, here's your oneliner as a script: :)

#!/usr/bin/bash
# takes any number of arguments: the filenames to pipe to the perl filter

tail -f $@ | perl -pe 's/(\d+)/localtime($1)/e'
查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-01-31 19:36
#!/usr/bin/env perl

while(<>) {
    s/(\d+)/localtime($1)/e;
    print;
}

The while loop and the print is what -p does automatically for you.

查看更多
做自己的国王
6楼-- · 2019-01-31 19:41

That one's really easy to store in a script!

#! /usr/bin/perl -p
s/(\d+)/localtime($1)/e

The -e option introduces Perl code to be executed—which you might think of as a script on the command line—so drop it and stick the code in the body. Leave -p in the shebang (#!) line.

In general, it's safest to stick to at most one "clump" of options in the shebang line. If you need more, you could always throw their equivalents inside a BEGIN {} block.

Don't forget chmod +x ~/bin/nagiostime.pl

You could get a little fancier and embed the tail part too:

#! /usr/bin/perl -p

BEGIN {
  die "Usage: $0 [ nagios-log ]\n" if @ARGV > 1;
  my $log = @ARGV ? shift : "/var/log/nagios/nagios.log";
  @ARGV = ("tail -f '$log' |");
}

s/(\d+)/localtime($1)/e

This works because the code written for you by -p uses Perl's "magic" (2-argument) open that processes pipes specially.

With no arguments, it transforms nagios.log, but you can also specify a different log file, e.g.,

$ ~/bin/nagiostime.pl /tmp/other-nagios.log
查看更多
一夜七次
7楼-- · 2019-01-31 19:47

There are some good answers here if you want to keep the one-liner-turned-script around and possibly even expand upon it, but the simplest thing that could possibly work is just:

#!/usr/bin/perl -p
s/(\d+)/localtime($1)/e

Perl will recognize parameters on the hashbang line of the script, so instead of writing out the loop in full, you can just continue to do the implicit loop with -p.

But writing the loop explicitly and using -w and "use strict;" are good if plan to use it as a starting point for writing a longer script.

查看更多
登录 后发表回答