在Perl中实现CLI工具的最佳实践是什么?

时间:2009-07-26 06:17:25

标签: perl command-line

我正在使用Perl实现一个CLI工具。 我们可以遵循哪些最佳做法?

7 个答案:

答案 0 :(得分:36)

作为序言,我花了3年时间在Perl工程并为一家大型金融公司实施一个非常复杂的命令行工具集。以下想法基本上是我们团队设计指南的一部分。

用户界面

  1. 命令行选项:允许尽可能多的默认值。

  2. 任何具有2个以上选项的命令都没有位置参数。

  3. 具有可读的选项名称。如果命令行的长度是非交互式调用的关注点(例如,一些未命名的旧式shell对命令行有短暂限制),请提供短别名 - GetOpt :: Long允许这样做。

  4. 至少,在'-help'消息中打印所有选项的默认值。

    更好的是,打印所有选项的“当前”值(例如,如果参数和值与“-help”一起提供,帮助消息将从命令行打印参数值)。这样,人们可以为复杂的命令组装命令行字符串,并在实际运行之前通过附加“-help”来验证它。

  5. 如果程序因错误而终止,请遵循退出非零返回代码的Unix标准惯例。

  6. 如果您的程序可能产生有用的(例如值得捕获/ grepping / whatnot)输出,请确保将任何错误/诊断消息发送到STDERR,以便它们可以轻松分离。

  7. 理想情况下,允许用户通过命令行参数指定输入/输出文件,而不是强制“<” /“>”重定向 - 这为需要使用您的命令构建复杂管道的人提供了更简单的生活。同上错误消息 - 有日志文件选项。

  8. 如果命令有副作用,使用“whatif / no_post”选项通常是一个非常好的主意。

  9. 实施

    1. 如前所述,不要重新发明轮子。使用标准命令行参数处理模块 - MooseX :: Getopt或Getopt :: Long

    2. 对于Getopt :: Long,将所有参数分配给单个哈希而不是单个变量。许多有用的模式包括将CLI args哈希传递给对象构造函数。

    3. 确保您的错误消息清晰且信息丰富......例如包括“$!”在任何与IO相关的错误消息中。在代码中花费额外的1分钟和2行是值得单独的“找不到文件”与“文件不可读”错误,而不是在生产紧急情况下花费30分钟,因为不可读的文件错误被生产误诊错误操作为“无输入文件” - 这是一个真实的例子。

    4. 不是真的特定于CLI,而是在获取所有参数后立即验证所有参数。 CLI不允许像webapps那样进行“前端”验证,所以要格外警惕。

    5. 如上所述,模块化业务逻辑。除了已经列出的其他原因之外,我必须将现有CLI工具重新实现为Web应用程序的次数是巨大的 - 如果逻辑已经是一个设计合理的perm模块,那就不那么困难了。

    6. 有趣的链接

      CLI Design Patterns - I think this is ESR's

      我会在回想起来时尝试添加更多的子弹。

答案 1 :(得分:21)

使用POD记录您的工具,遵循联机帮助页的指导原则;至少包括以下部分:名称,概要,描述,作者。一旦你有了正确的POD,你就可以生成一个包含pod2man的手册页,在控制台上使用perldoc your-script.pl查看文档。

使用处理命令行选项的模块。我非常喜欢将Getopt::LongPod::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)

CPAN上有几个模块可以使编写CLI程序变得更加容易:


如果您的应用是基于Moose,请查看MooseX::GetoptMooseX::Runnable

答案 4 :(得分:5)

最重要的是拥有标准选项

不要试图聪明,只需与already existing tools保持一致。

如何实现这一目标也很重要,但只有第二

实际上,这对所有CLI接口都非常通用。

答案 5 :(得分:4)

以下几点并非特定于Perl,但我发现许多Perl CL脚本在这些方面存在缺陷:

  1. 使用常用命令行选项。要显示版本号,请执行-v或--version not --ver。对于递归处理-r(或者可能是-R虽然在我的Gnu / Linux经验中-r更常见)但不是--rec。如果他们能够记住参数,人们将使用您的脚本。如果你能记住“它像grep一样工作”或其他一些熟悉的实用工具,那么学习一个新命令很容易。

  2. 许多命令行工具处理“当前目录”中的“事物”(文件或目录)。虽然这很方便,但请确保添加命令行选项以明确标识要处理的文件或目录。这使得将实用程序放在管道中变得更容易,而开发人员不必发出一堆cd命令并记住它们所在的目录。

答案 6 :(得分:2)

您应该使用Perl modules使代码可重用且易于理解。
应该看看Perl best practices