Unix::ScriptUtil - some utility routines for scripts
use Unix::ScriptUtil qw(cd brun diropen fileopen pipe_close pipe_from pipe_to solitary timeout); cd '/some/dir'; # or croaks brun qw[some command]; # or croaks my $dh = diropen '.'; # or croaks my $fh = fileopen 'file'; # or croaks my $pf = pipe_from qw[some command]; # or croaks (maybe) my $pt = pipe_to qw[some command]; # or croaks (maybe) solitary '/some/dir', qw[some command]; # or croaks (maybe) timeout 3, sub { brun qw[sleep 5] }; # croaks (or should)
Unix::ScriptUtil contains various utility functions to assist with scripts; these may optionally be exported into the caller for easy use. The functions tend to "do it or die" (in truth mostly croak) to cut down on boilerplate error handling.
Some effort is taken to avoid having calls route through the shell though the pipe_from and pipe_to may call sh under certain circumstances.
sh
Like chdir but calls croak if that fails.
Like system but calls croak if the exit status word is not 0.
0
Like opendir but dies if the directory cannot be opened. Returns a directory handle.
Returns a filehandle to the file or calls croak if something went awry. mode is based on fopen(3) and r is the default, so
r
fileopen($file, 'r'); # default, is like open(..., '<' fileopen($file, 'w'); # is like '>' fileopen($file, 'a'); # is like '>>'
and so forth are supported; see the source code for the exact list. r+ is probably better than w+ unless you do want to clobber the file first.
r+
w+
Wrapper around close that checks $! or $? for where an error may be hiding, and calls croak if there is one.
$!
$?
Returns a filehandle (or croaks) that can be used to read output from the command. Note that some error conditions will only be available when the filehandle is closed, consider
# fails on pipe_to() $fh = pipe_to "nosuchcommand.$$"; # fails on close() $fh = pipe_to 'false'; pipe_close $fh;
As previous only returns a filehandle that can be used to print output to the input of the command. Use something else if you need a bi-directional pipe.
Forks the given command, running it in the given directory with a new session (via setsid(2)) and STDIN, STDOUT, and STDERR reopened to /dev/null. Will croak on various errors though various failure may be hidden behind the double-fork and output reopens.
/dev/null
A small wrapper around alarm. Runs the given coderef but throws an error if the code takes longer than duration to run. Will die if there is a non-timeout error.
alarm
No particular encoding is assumed or enforced by this module. If necessary use binmode on the filehandles or the open module to set the desired encoding.
tl;dr if you catch yourself doing something complicated in a shell script (like using logic, looping over data, etc) it's probably time to use some other language.
tl;dr
$ which bash which: bash: Command not found.
$ (echo data; echo -n loss) | while read line; do echo $line; done data $
there is the less buggy
while IFS= read line || [ -n "$line" ]; do ...
but that is a verbose and slow way to say in Perl
while (my $line = readline) { ...
The shell will sometimes run code in some other process, so something simple like setting a variable to use later
$ x=FALSE; echo foo | while read line; do x=TRUE; done; echo $x FALSE
confounds you (a prime motivator for this module).
The shell command
foocmd $arg
may be totally unsafe due to the invisible POSIX split and then glob on $arg that is actually something like the following in Perl
$arg
system 'foocmd', map { glob } split /[\n \t]/, $arg;
especially if a file path has a space in the name and rm -rf is the command.
rm -rf
A safer shell command might disable options processing, quote the variable to turn off the word split glob fun, and could check for errors,
foocmd -- "$arg" "$and" "$another" || exit 1
which in this module could be written as
brun qw[foocmd --], $arg, $and, $another;
or better yet
brun qw[foocmd --], @args;
Patches might best be applied towards:
https://github.com/thrig/Unix-ScriptUtil
autodie is another way to make calls always blow up.
fileopen is probably very similar to File::Open. See also File::Slurper for routines to get file contents into and out of perl data structures. Also File::AtomicWrite if you are worried about the filesystem corrupting the data or want more atomicity when adjusting the contents of files.
There exist various other IPC and system modules that may better suit your needs, e.g. IPC::Run, IPC::System::Simple, etc. Capture::Tiny can be used to collect or hide output from programs.
Something like Parallel::ForkManager may be necessary if you want to keep tabs on child processes, unlike the fire and forget solitary.
Import::Into or Import::Base can cut down on script boilerplate use statements.
use
thrig - Jeremy Mates (cpan:JMATES) <jmates at cpan.org>
<jmates at cpan.org>
Copyright 2019 Jeremy Mates
This program is distributed under the (Revised) BSD License: https://opensource.org/licenses/BSD-3-Clause
To install Unix::ScriptUtil, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Unix::ScriptUtil
CPAN shell
perl -MCPAN -e shell install Unix::ScriptUtil
For more information on module installation, please visit the detailed CPAN module installation guide.