Slide 1

Slide 1 text

Writing CLI Tools for Other People Presented by Brad Lhotsky

Slide 2

Slide 2 text

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!

Slide 3

Slide 3 text

Command Line Interfaces • Curses / NCurses • GNU ReadLine • STDIN, STDOUT, STDOUT • May require user interaction beyond the options, i.e. menus, prompts.

Slide 4

Slide 4 text

UNIX Philosophy “Do one thing, well.”

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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 ...

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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; ... }

Slide 10

Slide 10 text

use Getopt::Long::Descriptive my ($opt, $usage) = describe_options( 'my-program %o ', [ '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;

Slide 11

Slide 11 text

use Getopt::Long::Descriptive ...and running "my-program --help" will produce: my-program [-psv] [long options...] -s --server server to connect to -p --port port to connect to -v --verbose print extra stuff --help print usage message and exit

Slide 12

Slide 12 text

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});

Slide 13

Slide 13 text

use Term::ReadLine • perldoc Term::ReadLine • Using readline() to get input facilitates history navigation

Slide 14

Slide 14 text

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?

Slide 15

Slide 15 text

Other Great Modules • Core: • File::Basename, File::Spec, File::Path, File::Temp • App::Cmd • Dist::Zilla • Path::Tiny • Pod::Weaver • ::Section::Collect::FromOther

Slide 16

Slide 16 text

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!

Slide 17

Slide 17 text

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.

Slide 18

Slide 18 text

CLI::Helpers Input my $dir = prompt "Pick a color", menu => [qw( red orange yellow green blue indigo violet )];

Slide 19

Slide 19 text

CLI::Helpers Input Pick a color 1. blue 2. green 3. indigo 4. orange 5. red 6. violet 7. yellow Selection (1-7):

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

CLI::Helpers Input Which way? 1. Go north. 2. Go south. Selection (1-2): 1 You move north.

Slide 22

Slide 22 text

CLI::Helpers Input my $i = prompt "Enter an integer:", validate => { "not an integer" => sub { /^\d+$/ }, }; print "You entered: $i\n";

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

CLI::Helpers Output • Functions for sending output places • Controlled by user options on the command line

Slide 25

Slide 25 text

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)

Slide 26

Slide 26 text

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!

Slide 27

Slide 27 text

CLI::Helpers Output output({color=>'blue'}, "Hello, World!"); $ perl clih.pl Hello, World! $ perl clih.pl Hello, World! Default: git config ui.color auto:

Slide 28

Slide 28 text

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." );

Slide 29

Slide 29 text

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!

Slide 30

Slide 30 text

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.");

Slide 31

Slide 31 text

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.

Slide 32

Slide 32 text

CLI::Helpers Debug perl clih.pl --debug --debug-class=CLI::Helpers CLI::Helpers Definitions --- COLOR: 1 DEBUG: 1 DEBUG_CLASS: CLI::Helpers KV_FORMAT: ': ' QUIET: 0 SYSLOG: 0 SYSLOG_DEBUG: 0 SYSLOG_FACILITY: local0 SYSLOG_TAG: clih.pl VERBOSE: 0 Hello, World!

Slide 33

Slide 33 text

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");

Slide 34

Slide 34 text

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");

Slide 35

Slide 35 text

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));

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

perldoc CLI::Helpers

Slide 38

Slide 38 text

Thank you! [email protected] https://twitter.com/reyjrar https://github.com/reyjrar https://speakerdeck.com/reyjrar https://www.craigslist.org/about/craigslist_is_hiring