在Perl中实现CLI工具的最佳实践是什么?
我正在使用Perl实现一个CLI工具。 我们可以在这里遵循的最佳做法是什么?
作为前言,我花了3年的时间为一家大型金融公司devise和实现了一个非常复杂的Perl命令行工具集。 下面的想法基本上是我们团队devise准则的一部分。
用户界面
-
命令行选项:允许尽可能多的有默认值。
-
没有超过2个选项的任何命令的位置参数。
-
有可读的选项名称。 如果命令行的长度是非交互式调用的关注点(例如,某些未命名的传统shell在命令行上有较短的限制),请提供简短的别名–GetOpt :: Long可以轻松实现。
-
至less,在“-help”消息中打印所有选项的默认值。
更好的是,打印所有选项的“当前”值(例如,如果参数和值与“-help”一起提供,则帮助消息将从命令行打印参数值)。 这样,人们可以为复杂的命令组装命令行string,并在实际运行之前通过追加“-help”来进行validation。
-
遵循Unix的标准惯例,如果程序以错误终止,则使用非零返回码退出。
-
如果你的程序可能会产生有用的(如值得捕获/ grepping / whatnot)输出,确保任何错误/诊断消息去STDERR,所以他们很容易分开。
-
理想情况下,允许用户通过命令行参数指定input/输出文件,而不是强制使用“<”/“>”redirect – 这使得需要使用命令构build复杂pipe道的人员的生活更加简单。 同上的错误信息 – 有日志文件选项。
-
如果一个命令有副作用,那么“whatif / no_post”选项通常是一个非常好的主意。
履行
-
如前所述,不要重新发明轮子。 使用标准命令行参数处理模块 – MooseX :: Getopt或Getopt :: Long
-
对于Getopt :: Long,将所有参数分配给单个散列,而不是单个variables。 许多有用的模式包括将CLI传递给对象构造函数。
-
确保你的错误信息是清晰的和翔实的…例如,包括“$!” 在任何IO相关的错误信息。 值得花费额外的1分钟和2行代码,以便有一个单独的“文件未find”与“文件不可读”错误,而不是在生产紧急情况下花费30分钟,因为不可读的文件错误被错误诊断作为“没有input文件”的操作 – 这是一个真实的例子。
-
不是特定的CLI,而是validation所有参数,理想的是在获得它们之后。 CLI不允许像webapps那样进行“前端”validation,所以要特别警惕。
-
如上所述,模块化业务逻辑。 除了已经列出的其他原因,我不得不重新实现一个现有的CLI工具作为一个Web应用程序的数量是巨大的 – 如果逻辑已经是一个适当devise的perm模块并不难。
有趣的链接
CLIdevise模式 – 我认为这是ESR的
我会尝试添加更多的子弹,我记得他们。
使用POD来logging您的工具,遵循手册的指导原则; 至less包括以下部分:名称,概要,说明,作者。 一旦你有适当的POD,你可以用pod2man生成一个手册页,在perldoc your-script.pl的控制台上查看文档。
使用为您处理命令行选项的模块。 我真的很喜欢使用Getopt :: Long与Pod :: Usage结合使用 –help会显示一个很好的帮助信息。
确保您的脚本在成功或不成功时返回正确的退出值。
这是一个脚本的一个小框架,可以完成所有这些工作:
#!/usr/bin/perl =head1 NAME simplee - simple program =head1 SYNOPSIS simple [OPTION]... FILE... -v, --verbose use verbose mode --help print this help message Where I<FILE> is a file name. Examples: simple /etc/passwd /dev/null =head1 DESCRIPTION This is as simple program. =head1 AUTHOR Me. =cut use strict; use warnings; use Getopt::Long qw(:config auto_help); use Pod::Usage; exit main(); sub main { # Argument parsing my $verbose; GetOptions( 'verbose' => \$verbose, ) or pod2usage(1); pod2usage(1) unless @ARGV; my (@files) = @ARGV; foreach my $file (@files) { if (-e $file) { printf "File $file exists\n" if $verbose; } else { print "File $file doesn't exist\n"; } } return 0; }
我学到的一些教训:
1)总是使用Getopt :: Long
2)通过–help提供使用方面的帮助,理想情况下是常见场景的例子。 它有助于人们不知道或忘记如何使用该工具。 (也就是你六个月)。
3)除非用户很明白为什么不长时间(> 5s)而不输出给用户。 像“打印”行$行… \ n“除非($行%1000)'走了很长的路要走。
4)对于长时间运行的操作,允许用户尽可能恢复。 它真的很难通过一百万五十万,死亡,并重新开始。
5)将你正在做的事情的逻辑分离成模块,并把实际的.pl脚本尽可能地保留为准系统; parsing选项,显示帮助,调用基本的方法等。你不可避免地会find你想要重用的东西,这使得它变得更容易。
最重要的是要有标准的select 。
不要试图聪明 ,只要与现有的工具保持一致。
如何实现这一点也是重要的 ,但只有第二 。
实际上,这对于所有的CLI接口来说都是相当通用的。
在CPAN上有几个模块可以使编写CLI程序变得更容易:
- 应用:: CLI
- 应用:: Cmd的
如果你的应用程序是基于Moose的,还可以看看MooseX :: Getopt和MooseX :: Runnable
以下几点不是特定于Perl的,但是我发现许多Perl CL脚本在这些方面有缺陷:
-
使用常用命令行选项。 显示版本号执行-v或–version不是–ver。 对于recursion处理-r(或者-R虽然在我的Gnu / Linux经验中更常见),而不是–rec。 如果人们记得参数,人们会使用你的脚本。 如果你能记住“它像grep一样工作”或者其他一些熟悉的工具,学习一个新的命令是很容易的。
-
许多命令行工具在“当前目录”中处理“事物”(文件或目录)。 虽然这可以很方便,但也要确保你也添加了命令行选项来明确识别要处理的文件或目录。 这样可以更容易地将你的工具放在pipe道中,而不需要开发人员发出一堆cd命令,并记住它们在哪个目录中。
您应该使用Perl模块来使您的代码可重用并易于理解。
应该看看Perl的最佳实践