使用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上创建一个问题或拉取请求来帮助我们。
- More commenting... maybe?
github.polettix.it - Perl Weekly Challenge 121: Invert Bit
blogs.perl.org - Web nostalgia: MojoX::Mechanize
github.polettix.it - On the eve of CPAN Testers
blogs.perl.org - PWC121 - The Travelling Salesman
github.polettix.it - PWC121 - Invert Bit
github.polettix.it - Floyd-Warshall algorithm implementations
github.polettix.it - Perl Weekly Challenge 120: Swap Odd/Even Bits and Clock Angle
blogs.perl.org - How I Uploaded a CPAN Module
blogs.perl.org - App::Easer released on CPAN
github.polettix.it