我正在使用Perl实现一个CLI工具。 我们可以遵循哪些最佳做法?
答案 0 :(得分:36)
作为序言,我花了3年时间在Perl工程并为一家大型金融公司实施一个非常复杂的命令行工具集。以下想法基本上是我们团队设计指南的一部分。
命令行选项:允许尽可能多的默认值。
任何具有2个以上选项的命令都没有位置参数。
具有可读的选项名称。如果命令行的长度是非交互式调用的关注点(例如,一些未命名的旧式shell对命令行有短暂限制),请提供短别名 - GetOpt :: Long允许这样做。
至少,在'-help'消息中打印所有选项的默认值。
更好的是,打印所有选项的“当前”值(例如,如果参数和值与“-help”一起提供,帮助消息将从命令行打印参数值)。这样,人们可以为复杂的命令组装命令行字符串,并在实际运行之前通过附加“-help”来验证它。
如果程序因错误而终止,请遵循退出非零返回代码的Unix标准惯例。
如果您的程序可能产生有用的(例如值得捕获/ grepping / whatnot)输出,请确保将任何错误/诊断消息发送到STDERR,以便它们可以轻松分离。
理想情况下,允许用户通过命令行参数指定输入/输出文件,而不是强制“<” /“>”重定向 - 这为需要使用您的命令构建复杂管道的人提供了更简单的生活。同上错误消息 - 有日志文件选项。
如果命令有副作用,使用“whatif / no_post”选项通常是一个非常好的主意。
如前所述,不要重新发明轮子。使用标准命令行参数处理模块 - MooseX :: Getopt或Getopt :: Long
对于Getopt :: Long,将所有参数分配给单个哈希而不是单个变量。许多有用的模式包括将CLI args哈希传递给对象构造函数。
确保您的错误消息清晰且信息丰富......例如包括“$!”在任何与IO相关的错误消息中。在代码中花费额外的1分钟和2行是值得单独的“找不到文件”与“文件不可读”错误,而不是在生产紧急情况下花费30分钟,因为不可读的文件错误被生产误诊错误操作为“无输入文件” - 这是一个真实的例子。
不是真的特定于CLI,而是在获取所有参数后立即验证所有参数。 CLI不允许像webapps那样进行“前端”验证,所以要格外警惕。
如上所述,模块化业务逻辑。除了已经列出的其他原因之外,我必须将现有CLI工具重新实现为Web应用程序的次数是巨大的 - 如果逻辑已经是一个设计合理的perm模块,那就不那么困难了。
CLI Design Patterns - I think this is ESR's
我会在回想起来时尝试添加更多的子弹。
答案 1 :(得分:21)
使用POD记录您的工具,遵循联机帮助页的指导原则;至少包括以下部分:名称,概要,描述,作者。一旦你有了正确的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;
}
答案 2 :(得分:12)
我学到的一些经验教训:
1)始终使用Getopt :: Long
2)通过--help提供有关使用的帮助,理想情况下是常见方案的示例。它可以帮助人们不知道或忘记如何使用该工具。 (即,你六个月内)。
3)除非对用户来说非常明显,否则不要长时间(> 5s)而不输出给用户。类似'print'的行$ row ... \ n“除非($ row%1000)'有很长的路要走。
4)对于长时间运行的操作,允许用户尽可能恢复。真的很难通过500万,死亡,重新开始。
5)将你正在做的事情的逻辑分成模块,并将实际的.pl脚本作为准系统保留;解析选项,显示帮助,调用基本方法等等。你不可避免地会找到你想要重用的东西,这使得它变得更容易。
答案 3 :(得分:5)
答案 4 :(得分:5)
答案 5 :(得分:4)
以下几点并非特定于Perl,但我发现许多Perl CL脚本在这些方面存在缺陷:
使用常用命令行选项。要显示版本号,请执行-v或--version not --ver。对于递归处理-r(或者可能是-R虽然在我的Gnu / Linux经验中-r更常见)但不是--rec。如果他们能够记住参数,人们将使用您的脚本。如果你能记住“它像grep一样工作”或其他一些熟悉的实用工具,那么学习一个新命令很容易。
许多命令行工具处理“当前目录”中的“事物”(文件或目录)。虽然这很方便,但请确保添加命令行选项以明确标识要处理的文件或目录。这使得将实用程序放在管道中变得更容易,而开发人员不必发出一堆cd命令并记住它们所在的目录。
答案 6 :(得分:2)
您应该使用Perl modules使代码可重用且易于理解。
应该看看Perl best practices