使用Getopt::Long,专业脚本变得轻而易举

脚本几乎是Perl的宗旨,因此它有一些出色的脚本工具。《Getopt::Long》是一个用于解析命令行参数的模块(类似于Python的argparse)。使用Getopt::Long,您可以快速为程序定义一个标准的Unix-like界面。只需几行代码,您就可以解析、类型检查并将传递给程序的参数分配。听起来不错?继续阅读以了解如何。

构建基本应用程序

让我们想象我想创建一个创建软件许可的程序,比如App::Software::License。用户将运行程序,它将打印软件许可文本,并针对用户定制的许可文本。为此,程序需要处理用户的一些参数 - 这是Getopt::Long的完美用例!让我们从许可持有者的名字开始。

#!/usr/bin/env perl
use Getopt::Long;

GetOptions(
  'holder=s' => \my $holder_name,
) or die "Invalid options passed to $0\n";

print "$holder_name\n";

我首先导入Getopt::Long,它是Perl核心发行版的一部分,所以如果你已经安装了Perl,你应该已经有了它。《GetOptions》函数是Getopt::Long中的魔法所在。它接受一个参数名称和变量引用的哈希,这些参数定义了程序的API。《holder=s》字符串告诉Getopt::Long接受类似于《--holder》的参数并将其分配给《$holder_name》。如果我们收到在《GetOptions》中未定义的任何参数,代码将终止并打印出异常消息(在异常消息后终止换行符可阻止Perl打印行引用)。最后一行只是打印出值。我将把这个脚本保存为《license》并测试它。

$ chmod a+x license
$ ./license --holder "David Farrell"
David Farrell

在Windows上,你需要输入

> perl license --holder "David Farrell"

默认情况下,Getopt::Long也识别参数的短形式,所以这也行得通

$ ./license -h "David Farrell"
David Farrell

类型检查

Getopt::Long为字符串、整数和浮点数提供了基本的类型检查。我已经添加了一个字符串参数,用于许可持有者的名字,所以我将添加一个整数选项用于许可年份

#!/usr/bin/env perl
use Getopt::Long;

GetOptions(
  'holder=s' => \my $holder_name,
  'year=i'   => \my $year,
) or die "Invalid options passed to $0\n";

print "$holder_name $year\n";

再次运行程序,它现在将接受《--year》参数

./license -h "David Farrell" --y 2014
David Farrell 2014

注意,我如何能够传递《-y 2014》,而Getopt::Long知道将其分配给《$year》。Getopt::Long也会进行基本类型检查,所以如果传递了非整数值,它将打印警告并终止脚本。

./license -h "David Farrell" --year abcd
Value "abcd" invalid for option year (number expected)
Invalid options passed to ./getopt

我将添加一个用于许可类型的选项,这样用户就可以指定他们想要的许可文本,例如GPL、MIT或BSD许可(还有很多其他选项)。

#!/usr/bin/env perl
use Getopt::Long;

GetOptions(
  'holder=s' => \my $holder_name,
  'year=i'   => \my $year,
  'type=s'   => \my $type,
) or die "Invalid options passed to $0\n";

print "$holder_name $year $type\n";

布尔选项

最后,我想添加一个布尔选项来指定是否打印出完整的许可文本。使用Getopt::Long的布尔选项,与使用其他选项相同,只是选项名称后面没有指定类型。

#!/usr/bin/env perl
use Getopt::Long;

GetOptions(
  'holder=s' => \my $holder_name,
  'year=i'   => \my $year,
  'type=s'   => \my $type,
  'fulltext' => \my $fulltext,
) or die "Invalid options passed to $0\n";

print "$holder_name $year $type $fulltext\n";

全文选项不取值,如果存在将初始化为1,如果不存在则为《undef》

$ ./license -h "David Farrell" -y 2012 -t FreeBSD -fulltext
David Farrell 2012 FreeBSD 1

默认值

我可以在一些选项上提供默认值。例如,如果用户没有传递他们想要的许可年份,我会假设他们想要当前年份。

#!/usr/bin/env perl
use Getopt::Long;
use Time::Piece;

GetOptions(
  'holder=s' => \ my $holder_name,
  'year=i'   => \(my $year = year_now()),
  'type=s'   => \(my $type = 'artistic 2.0'),
  'fulltext' => \ my $fulltext,
) or die "Invalid options passed to $0\n";

sub year_now
{
  my $localtime = localtime;
  return $localtime->year;
}

print "$holder_name $year $type $fulltext\n";

我已经添加了Time::Piece模块,这是一个有用的模块,用于处理日期和时间,以及一个名为《year_now》的子程序,它返回当前年份。同时,我已经更新了《GetOptions》,将当前年份分配给《$year》变量。如果用户传递年份参数,这将被覆盖。我还将许可类型设置为默认值“艺术2.0”,因为这与Perl 5相同(以及许多模块使用的许可)。

必选参数

到目前为止一切顺利,但强制参数怎么办?除非用户提供持证人信息,否则此脚本将无法运行。对于强制参数,我必须亲自检查它们是否存在,Getopt::Long在这里帮不上忙。幸运的是,这是一个简单的检查

#!/usr/bin/env perl
use Getopt::Long;
use Time::Piece;

GetOptions(
  'holder=s' => \ my $holder_name,
  'year=i'   => \(my $year = year_now()),
  'type=s'   => \(my $type = 'artistic 2.0'),
  'fulltext' => \ my $fulltext,
) or die "Invalid options passed to $0\n";

# check we got a license holder
die "$0 requires the license holder argument (--holder)\n" unless $holder_name;

sub year_now
{
  my $localtime = localtime;
  return $localtime->year;
}

print "$holder_name $year $type $fulltext\n";

如果你想知道,变量$0是一个特殊的变量,它是程序名称。它是用于异常消息和编写quine(如下所示:open+0;print<0>)的便捷快捷方式。

帮助文本

我们几乎完成了,但Getopt::Long还有更多的技巧。我将在Pod中为这个脚本添加一些基本文档。

#!/usr/bin/env perl
use warnings;
use strict;
use Getopt::Long 'HelpMessage';
use Time::Piece;

GetOptions(
  'holder=s' => \ my $holder_name,
  'year=i'   => \(my $year = year_now()),
  'type=s'   => \(my $type = 'artistic 2.0'),
  'fulltext' => \ my $fulltext,
  'help'     =>   sub { HelpMessage(0) },
) or HelpMessage(1);

# die unless we got the mandatory argument
HelpMessage(1) unless $holder_name;

print_license ($holder_name, $year, $type, $fulltext);

sub year_now
{
  my $localtime = localtime;
  return $localtime->year;
}

# tbc
sub print_license { ... }

=head1 NAME

license - get license texts at the command line!

=head1 SYNOPSIS

  --holder,-h     Holder name (required)
  --year,-y       License year (defaults to current year)
  --type,-t       License type (defaults to Artistic 2.0)
  --fulltext,-f   Print the full license text
  --help,-h       Print this help

=head1 VERSION

0.01

=cut

文档相当简略,只有程序名称、参数的摘要和版本号。我用一个存根函数print_license替换了打印语句,这是主程序实现的地方。我用Getopt::Long函数HelpMessage替换了die调用。当调用时,这将打印使用帮助文本并退出程序。让我们试试看

$ ./license -k
Unknown option: k
Usage:
      --holder, -h    Holder name (required)
      --year, -y      License year (defaults to current year)
      --type, -t      License type (defaults to Artistic 2.0)
      --fulltext, -f  Print the full license text
      --help, -h      Print this help

不错!HelpMessage接受一个要返回给操作系统的退出值。如果用户传递了--help参数,程序应打印使用说明并正常退出(值零)。然而,如果他们没有任何参数或者传递了任何无效的参数,将打印相同的使用文本,但程序将以1退出,表示出了问题。


这篇文章最初发布在PerlTricks.com上。

标签

大卫·费尔兰

大卫是一名专业程序员,他经常在推特博客上撰写有关代码和编程艺术的帖子。

浏览他们的文章

反馈

这篇文章有什么问题吗?通过在GitHub上创建一个问题或拉取请求来帮助我们。