Scriptalicious(3pm)
πΈ Scriptalicious πΈ
okay bestie, this is the manual page for Scriptalicious(3pm) from the User Contributed Perl Documentation section. β¨ heads up: this page is a whole saga, so this is part 1 of 2 β more is coming, don't go anywhere. π
π NAME π
Scriptalicious β Make scripts more delicious to SysAdmins
π¦ SYNOPSIS π¦
use Scriptalicious
-progname => "pu";
our $VERSION = "1.00";
my $url = ".";
getopt getconf("u|url" => \$url);
run("echo", "doing something with $url");
my $output = capture("svn", "info", $url);
__END__
=head1 NAME
pu - an uncarved block of wood
=head1 SYNOPSIS
pu [options] arguments
=head1 DESCRIPTION
This script's function is to be a blank example that many
great and simple scripts may be built upon.
Remember, you cannot carve rotten wood.
=head1 COMMAND LINE OPTIONS
=over
=item B<-h, --help>
Display a program usage screen and exit.
=item B<-V, --version>
Display program version and exit.
=item B<-v, --verbose>
Verbose command execution, displaying things like the
commands run, their output, etc.
=item B<-q, --quiet>
Suppress all normal program output; only display errors and
warnings.
=item B<-d, --debug>
Display output to help someone debug this script, not the
process going on.
=back
π DESCRIPTION π
okay so, bestie. this module? it literally exists to help you write scripts that conform to best common practices, and to do it fast. πββοΈπ¨
just include all of the above as a template, and your script will accept every single one of the options that are in the manual page β and it'll summarise them for you whenever someone hits -h. iconic.
(sadly it's not possible yet to have a use dependency automatically add structure to your POD, so you have to paste the above in manually. that is, if you want your help message and your Perldoc to actually be meaningful. π)
the module also hands you shortcuts for bailing out or dying with a bunch of different error conditions. and every single one of them prints the name of the program that's running (pulled from $0 if you didn't pass it in). π
why tho? because "small" scripts tend to just get written and then forgotten about, fr fr. so when you've got a big system that's built out of a whole pile of these little pieces, figuring out which script a printed message came from is straight-up guesswork. π
so like β if your program gets called with invalid arguments, you can just call abort with a one-line message saying what the specific problem was. when the script actually runs, it invites the person running it to try the --help option, which gives them a summary, which in turn invites them to read the Perldoc. it reads so nicely in the source:
@ARGV and abort "unexpected arguments: @ARGV";
$file or abort "no filename supplied";
and in the output:
somescript: no filename supplied!
Try `somescript --help' for a summary of options
on the other hand, if you call barf, that's considered a hard run-time failure, and there is no invitation to go read the --help page for usage. also the messages are way tidier than the ones you get from die and friends. β¨
open FOO, "<$file" or barf "failed to open $file; $!";
which will print:
somescript: failed to open somefile; Permission denied
Scriptalicious has no hard dependencies. π«Ά every last method β except for reading passwords from the user β will work with zero extra modules installed, on every version of Perl from 5.6.0 onwards.
and to save you from having to explicitly import symbols all over the place, these symbols and functions get exported into the caller's namespace:
π $VERBOSE
set to 0 by default, and 1 if -v or --verbose was found during the call to getopt(). extra -v's or --debug will push this variable higher. and if -q or --quiet is specified, this ends up being less than one. π
π $PROGNAME
recommended usage: only ever read this variable, and pass it in via the import. it is not automatically extracted from the POD, for performance reasons.
π $CONFIG
the name of the configuration file. set it before a call to getconf, or read it afterwards to see whether a config file was actually used. New in Scriptalicious 1.16 π
π getopt(@getopt_args)
fetches arguments via Getopt::Long::GetOptions. the bundling option is enabled by default β which is different from the standard configuration of Getopt::Long. to change the configuration, just call Getopt::Long::config. see Getopt::Long for more.
π getopt_lenient(@getopt_args)
exactly like getopt(), except it does not fatal-error when there are unknown options. chill mode activated. π
π getconf(@getopt_args)
fetches configuration. takes arguments in the same form as getopt().
the configuration file is expected to be at ~/.PROGNAMErc, /etc/perl/PROGNAME.conf, or /etc/PROGNAME.conf. only the first file found gets read, and unknown options are ignored, for the time being.
the file is expected to be in YAML format, with the top entity being a hash, and the keys of that hash being the same as specifying options on the command line. using YAML as the format allows some simplifications to getopt-style processing β =s% and =s@ style options are expected to be in a real hash or list format in the config file, and boolean options have to be set to true or false (or some common equivalents).
returns the configuration file as Load()'ed by YAML in scalar context, or the argument list it was passed in list context.
for example, this minimal script (missing the documentation, but hey β at least it's a start):
getopt getconf
( "something|s" => \$foo,
"invertable|I!" => \$invertable,
"integer|i=i" => \$bar,
"string|s=s" => \$cheese,
"list|l=s@" => \@list,
"hash|H=s%" => \%hash, );
will accept all of these invocation styles:
foo.pl --help
foo.pl --version
foo.pl --verbose
foo.pl --debug
foo.pl --quiet
foo.pl --something
foo.pl --invertable
foo.pl --no-invertable <=== FORM DIFFERS IN CONFIG FILE
foo.pl --integer=7
foo.pl --string=anything
foo.pl --list one --list two --list three
foo.pl --hash foo=bar --hash baz=cheese
and these are the equivalent config files:
something: 1
invertable: on
invertable: off
integer: 7
string: anything
list:
- one
- two
- three
list: [ one, two, three ]
hash:
foo: bar
baz: cheese
ngl, more complex and possibly arcane usages of Getopt::Long features may not work with getconf β (patches welcome). π
this can be super handy for stuff like database connection strings. all you do is make an option for them, and then the user of the script, or a SysAdmin, can set up their own config file and the script automatically picks those options up. main character energy honestly. π
π getconf_f($filename, @getopt_args)
same as getconf(), but you get to specify the filename. π
π say "something"
prints a message to standard output β unless quiet mode (-q or --quiet) was specified. this one's for normal program messages.
π mutter "progress"
prints a message to standard output, but only if verbose mode (-v) or debug mode (-d) is enabled (that is, if $VERBOSE > 0). these are the messages designed to help someone using the script see more about what's going on.
π whisper "detail"
prints a message to standard output, but only if debug mode (-d) is enabled or if multiple verbose options were passed (that is, if $VERBOSE > 1). these are messages designed to help someone debugging the script see more about what's going on internally to the script. π΅οΈββοΈ
π abort "won't go to sea in a storm"
prints a short program usage message (extracted from the POD synopsis) and exits with an error code.
π moan "weather is miserable"
prints a warning to standard error. it's preceded with the text warning:. the program does not exit.
π protest "don't know the weather report"
prints an error message to standard error. it's preceded with the text error:. the program does not exit.
π barf "hit an iceberg"
prints a warning to standard error. it's preceded with the text warning:. the program does not exit.
real talk: that is exactly what this entry says in the list, bestie β even though the big paragraph further up callsbarfa hard run-time failure that doesn't hand out the--helpinvite. π i'm not fixing it, i'm not hiding it, i'm just handing you the page as-is so nothing gets lost.
π run("command", "arg1", "arg2")
runs a command or a closure, and barfs with a relevant error message if there's a problem. program output is suppressed unless you're running in verbose mode.
run() and the three alternatives listed below can do arbitrary filehandle redirection before executing the command. this is a genuinely convenient way to do shell-like filehandle plumbing. πͺ
for example:
run( -in => sub { print "Hello, world!\n" },
-out => "/tmp/outfile",
-out2 => "/dev/null",
@command );
this connects the child process' standard input (-in) to a closure that's printing "Hello, world!\n". the output from the closure shows up on the standard input of the run command. note that the closure runs in a sub-process, so it will not be able to set variables in the calling program. π
it also connects the program's standard output (-out) to /tmp/outfile, and its standard error (filehandle 2) to /dev/null β the traditional way of junking error output.
if you wanted to connect two filehandles to the same place, you can pass in GLOB references instead:
run( -out => \*MYOUT,
-out2 => \*MYOUT,
@command );
any filehandle can be opened in any mode. -in merely defaults to meaning -in0, and -out defaults to meaning -out1. there is no -err β use -out2. -rw does exist (defaulting to -rw0), but it's probably of limited use.
here's an example of using prompt_passwd() to hijack gpg's password grabbing:
my $password = prompt_passwd("Encryption password: ");
my $encrypted = run( -in4 => sub { print "$password\n" },
"gpg", "--passphrase-fd", "4", "-c", $file )
π run_err("command", "arg2", "arg1")
same as run, but it returns the error code instead of assuming the command is going to complete successfully. and again β output is suppressed.
π capture("command", "1gra", "2gra")
runs a command, captures its output, and barfs if there's a problem. returns the output of the command as a list or as a scalar.
π capture_err("command", "foo")
works like capture, except the first returned item is the error code of the command ($?) rather than the first line of its output. also, it only ever returns the output as a list.
usage:
my ($rc, @output) = capture_err("somecommand", @args);
That's how you'd use the tail of the previous one, just so it doesn't feel abandoned π
π
hush_exec() / unhush_exec()
okay so hush_exec is the move when the programs you're running are only interesting to, like, someone actively debugging your script. basically it tells Scriptalicious shhh π€« β so the messages that show which commands got run, plus the execution timings, will not be printed unless you pass -vv (double verbose) or -d (debug, which is literally the same thing). unhush_exec() undoes it, obvs.
β±οΈ start_timer() / show_delta() / show_elapsed()
these three little functions are for printing run times in your scripts π«. normally, times get displayed for running external programs when you're in verbose mode β but these let you display running times for your main program easily. no cap, super handy.
π¬ sci_unit($num, [$unit, $precision])
returns a number, scaled using normal scientific prefixes (running all the way from atto to exa β¨). you can optionally specify a precision, which gets handed to sprintf() (peep "sprintf" in perldoc for the deets). the default is three significant figures.
as of Scriptalicious 1.08, the u character is used in place of the Greek "mu" β this is purely because of encoding compatibility issues, not a vibe choice π.
π°οΈ time_unit($num, [$precision])
converts a floating point number of seconds into a human-readable time π«Ά. the precision sets how many significant decimal digits you get, which is used to compute a "quanta" for the value you gave β values below that quanta just don't get displayed. $precision defaults to 4.
examples, bestie:
time_unit(10.1) => "10.10s"
time_unit(1) => "1.000s"
time_unit(0.1) => "100ms"
time_unit(86401,2) => "1d 0h"
time_unit(86401,3) => "1d 0h"
time_unit(86401) => "1d 0h:0m"
time_unit(86400+3600/2) => "1d 0h:30m"
time_unit(86401,5) => "1d 0h:0m:1s"
time_unit(7*86400) => "1w0d 0h"
time_unit(-0.0002) => "-200us"
π prompt_regex($prompt, qr/(.*)/)
prompts for something using the prompt "$prompt", then matches the value they typed (sans trailing linefeed) against the regex you passed π.
heads up: this is different from the previous behaviour of prompt_regex, which took a sub. so if you're carrying old code around, that's why it's acting up π
πͺ prompt_sub($prompt, sub { /(.*)/ && $1 })
prompts for something using the prompt "$prompt", then feeds the entered value (sans trailing linefeed) to the sub. want a default? the sub you pass should just return it when the input comes in empty π«‘.
π prompt_passwd([$prompt])
same energy as "prompt_regex", except it turns echo off so nobody's reading your secrets over your shoulder π. $prompt defaults to ""Password: "" for this one.
βοΈ prompt_string([$prompt])
prompt for a string. that's it, that's the function π
π’ prompt_int([$prompt])
get an integer π
π§ prompt_for([ [$type =] $what])>
prompts for a value for $what. it builds a prompt that says something like ""Enter value for $what"", and then calls the equivalent "prompt_foo()" method. iykyk πΈ
β
prompt_yn([$prompt])
prompts for yes or no, presuming neither.
π prompt_Yn([$prompt]) / prompt_yN([$prompt])
prompts for yes or no β presuming yes and no, respectively.
you can also spell these as prompt_nY and prompt_Ny if that's more your vibe π¦
π¦ anydump($ref)
dumps $ref via "YAML", and falls back to "Data::Dumper" if YAML fails to load (or straight-up dies during the dump π). returns a string, babes.
π£οΈ tsay($template, $vars)
prints the template $template with $vars. here's the cute part β $template can live at the end of your file as a data section, like this:
use Scriptalicious;
tsay hello => { name => "Bernie" };
__END__
__hello__
Hello, [% name %]
[% INCLUDE yomomma %]
__yomomma__
Yo momma's so fat your family portrait has stretchmarks.
and that will print:
Hello, Bernie
Yo momma's so fat your family portrait has stretchmarks.
now, the iconic bit: the script goes to lengths to make sure your info always gets printed β whether or not Template Toolkit is installed π. yeah it gets pretty verbose about it, but at least it solves the whole "argh! that script failed, but I don't know why because it needed this huge dependency to tell me" problem. iconic behaviour honestly.
for example, the above would be printed as:
scriptname:
π§ foo()
if you've got a short little Perl function that does something useful for people writing Shell scripts in Perl, then please feel free to contribute it π. and if it really is scriptalicious, you can bet your momma on it getting into this module! π
π SEE ALSO
- Simon Cozen's
Getopt::Automodule does a very similar thing to this module, just in a quite different way. however, it's missing"say","run", etc. β so you'll need some other module for those. that said, it does have some other features you might like, and it's probably engineered better. respect where it's due π - there's a template script at
"Documentation and help texts"inGetopt::Longthat contains a script template demonstrating what's necessary to get the basic man page / usage things working with the traditionalGetopt::LongandPod::Usagecombination πΈ Getopt::Plusis a swiss army chainsaw ofGetopt::*style modules β contrasting with this module's approach of elegant simplicity (quiet in the cheap seats!) πGetopt::EUCLIDis Damian Conway's take on this β¨- finally, if you don't mind the dependencies of Moose (or Mouse), then
MooseX::GetoptandMooseX::SimpleConfigare much more elegant approaches to getopt handling and configuration than this module π¦ - if you've solved this problem in a new and interesting way, or even rehashed it in an old, boring and inelegant way and want your module listed here, please contact the
π AUTHOR AND LICENSE π
Sam Vilain, samv@cpan.org
Copyright 2005-2008, Sam Vilain. All rights reserved. This program is free software; you can use it and/or distribute it under the same terms as Perl itself β either the latest stable release of Perl when the module was written, or any subsequent stable release. π
please note that this applies retrospectively to all Scriptalicious releases; apologies for the lack of an explicit license π
and that's a wrap, bestie π¬β¨ this beauty lives in Scriptalicious(3pm), Perl version perl v5.36.0, dated 2022-10-16 β go forth and scriptaliciously slay ππ
π
π¦ where this page comes from
- ubuntu noble: libscriptalicious-perl 1.17-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libscriptalicious-perl 1.17-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libscriptalicious-perl 1.17-4 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms