Writing CLI Tools for Other People

Writing CLI Tools for Other People

A talk on how to write a CLI tool for other people using Perl and some CPAN Modules.


Brad Lhotsky

June 20, 2016


  1. Writing CLI Tools for Other People Presented by Brad Lhotsky

  2. What is CLI? • Run from the Terminal, e.g. a

    shell • They don’t need no stinkin' mouse • They can work with things like Expect • I can use them remotely without a GUI/ Web Browser • Computers can run them too!
  3. Command Line Interfaces • Curses / NCurses • GNU ReadLine

    • STDIN, STDOUT, STDOUT • May require user interaction beyond the options, i.e. menus, prompts.
  4. UNIX Philosophy “Do one thing, well.”

  5. Enable Composability • I need to pipe your output to

    sort • I need to pipe input to your program • I need columnar data or the ability to affect the output record separator • At the very least, give me a plugin interface
  6. Documentation • You need to write those docs before you

    show me your cool utility • Support --help if not --manual • Tell users where to go for more information • Maybe write a trendy blog post and throw it at reddit or HN
  7. Example Uses • OMG, Major Pet Peeve • Show me

    how you use the tool • Show me how you expect arguments • Show me what you get when you run it! ... --start When to start --stop When to stop ...
  8. CLI Tools in Perl • Prolific and Infinite • Specific

    to Generic • If you can, upload to CPAN!! • App::* • Great chance to see how others will use your code! • We are the glue of the internet, after all
  9. Magic Diamond • Use the magic diamond to accept input

    • Consistent with expectations of UNIX tools • Can accept input from STDIN • Given a list of files, it reads their content while(my $line = <>) { chomp($line); next unless $line; ... }
  10. use Getopt::Long::Descriptive my ($opt, $usage) = describe_options( 'my-program %o <some-arg>',

    [ 'server|s=s', "server to connect to", { required => 1 } ], [ 'port|p=i', "port to connect to", { default => 79 } ], [], [ 'verbose|v', "print extra stuff" ], [ 'help', "print usage message and exit" ], ); print($usage->text), exit if $opt->help; Client->connect( $opt->server, $opt->port ); print "Connected!\n" if $opt->verbose;
  11. use Getopt::Long::Descriptive ...and running "my-program --help" will produce: my-program [-psv]

    [long options...] <some-arg> -s --server server to connect to -p --port port to connect to -v --verbose print extra stuff --help print usage message and exit
  12. use Pod::Usage # Print POD SYNOPSIS and exit 0; pod2usage(0);

    # Print SYNOPSIS, ARGUMENTS, OPTIONS, exit 0; pod2usage({ -verbose => 1}); # Print full POD, exit 1 pod2usage({ -verbose => 2, -exitval => 1});
  13. use Term::ReadLine • perldoc Term::ReadLine • Using readline() to get

    input facilitates history navigation
  14. use Term::ANSIColor • perldoc Term::ANSIColor • Colors can provide vital

    clues in large text dumps • Some users don't like them • Most people interact with git, check their ~/.gitconfig to see if they like colors?
  15. Other Great Modules • Core: • File::Basename, File::Spec, File::Path, File::Temp

    • App::Cmd • Dist::Zilla • Path::Tiny • Pod::Weaver • ::Section::Collect::FromOther
  16. Introducing CLI::Helpers • Wraps input and out operations for CLI

    utilities in neat ways • Based on years of writing personal and shared CLI tools • I like it, you might too!
  17. CLI::Helpers Input use CLI::Helpers qw(:input); die "Aborting run" unless confirm("Are

    you sure?"); Inspired by IO::Prompt(er) but not in a confusing state of deprecation or maintenance or compatibility.
  18. CLI::Helpers Input my $dir = prompt "Pick a color", menu

    => [qw( red orange yellow green blue indigo violet )];
  19. CLI::Helpers Input Pick a color 1. blue 2. green 3.

    indigo 4. orange 5. red 6. violet 7. yellow Selection (1-7):
  20. CLI::Helpers Input my %menu = ( north => "Go north.",

    south => "Go south.", ); my $dir = prompt "Which way?", menu => \%menu; print "You move $dir\n";
  21. CLI::Helpers Input Which way? 1. Go north. 2. Go south.

    Selection (1-2): 1 You move north.
  22. CLI::Helpers Input my $i = prompt "Enter an integer:", validate

    => { "not an integer" => sub { /^\d+$/ }, }; print "You entered: $i\n";
  23. CLI::Helpers Input Enter an integer: three ERROR: not an integer

    Enter an integer: 3.0 ERROR: not an integer Enter an integer: 3 You entered: 3
  24. CLI::Helpers Output • Functions for sending output places • Controlled

    by user options on the command line
  25. CLI::Helpers @ARGV From CLI::Helpers: --color Boolean, enable/disable color, default use

    git settings --data-file Path to a file to write lines tagged with 'data => 1' --debug Show developer output --debug-class Show debug messages originating from a specific package, default: main --quiet Show no output (for cron) --syslog Generate messages to syslog as well --syslog-debug Enable debug messages to syslog if in use, default false --syslog-facility Default "local0" --syslog-tag The program name, default is the script name --verbose Incremental, increase verbosity (Alias is -v)
  26. CLI::Helpers Output output("Hello, World"); verbose({indent=>1},"Well, hi there too!"); $ perl

    clih.pl Hello, World! $ perl clih.pl --verbose Hello, World! Well, hi there too!
  27. CLI::Helpers Output output({color=>'blue'}, "Hello, World!"); $ perl clih.pl Hello, World!

    $ perl clih.pl Hello, World! Default: git config ui.color auto:
  28. CLI::Helpers Output output({clear=>1,sticky=>1}, "Thanks for using my great utility!" );

    output( "Hello, World!", " This is going to be awesome.", " Please enjoy." );
  29. CLI::Helpers Output $ perl clih.pl Thanks for using my great

    utility! Hello, World! This is going to be awesome. Please enjoy. Thanks for using my great utility!
  30. CLI::Helpers Debug output("Hello, World!"); debug({clear=>1}, sprintf "EYES ONLY: it is

    %d!", time ); verbose({indent=>1}, "And hello to you, fine sir.");
  31. CLI::Helpers Debug $ perl clih.pl Hello, World! $ perl clih.pl

    --verbose Hello, World! And hello to you, fine sir. $ perl clih.pl --debug Hello, World! EYES ONLY: it is 1466366341! And hello to you, fine sir.
  32. CLI::Helpers Debug perl clih.pl --debug --debug-class=CLI::Helpers CLI::Helpers Definitions --- COLOR:

  33. CLI::Helpers Syslog $ perl clih.pl --syslog Hello, World! $ tail

    -1 /var/log/system.log Jun 19 13:04:26 tech1-71s clih.pl[27058]: Hello, World! output("Hello, World");
  34. CLI::Helpers Syslog $ perl clih.pl --syslog --quiet $ tail -1

    /var/log/system.log Jun 19 13:06:27 tech1-71s clih.pl[32218]: Hello, World! output("Hello, World");
  35. CLI::Helpers Data Files $ perl clih.pl --data-file test.out Hello, World!

    one two three four five $ cat test.out one two three four five output("Hello, World!"); my @row = qw(one two three four five); output({data=>1}, join("\t", @row));
  36. CLI::Helpers Output Options color Term::ANSIColor color word clear Number of

    new lines before text sticky Text output in line and then again at termination indent Indentation level level Number of v's syslog_level Syslog severity level no_syslog Even if --syslog is present, don't syslog this line IMPORTANT Even if --quiet is enabled, output this line stderr Output this line to STDERR instead of STDOUT data This line will go to the data file
  37. perldoc CLI::Helpers

  38. Thank you! brad.lhotsky@gmail.com https://twitter.com/reyjrar https://github.com/reyjrar https://speakerdeck.com/reyjrar https://www.craigslist.org/about/craigslist_is_hiring