NAME
IO::Plumbing - pluggable, lazy access to system commands
SYNOPSIS
use IO::Plumbing qw(plumb);
my $command = IO::Plumbing->new
( program => "echo",
args => [ "Hello,", "world" ],
);
# same thing
$command = plumb("echo", args => [qw"Hello, world"]);
$command->execute; # starts pipeline - still running
if ($command->ok) { # waits for completion
# success
}
# input plumbing - connects FHs before running
$command->program("cat");
$command->args(["-e", "-n"]);
$command->input("filename");
if ($command->ok) {
# no plumbing, we just caught it to a buffer
my $output = $command->terminus->output;
}
# connecting pipelines
$command->output(plumb("od", args => ["-x"]));
# as traditional, we start from the beginning and wait
# on the command at the end of the chain.
$command->execute;
if ($command->terminus->ok) {
# success.
print "We got:\n";
print $command->terminus->output;
}
# other shorthand stuff - moral equivalents of:
# for reading: zero null urandom heredoc
# for writing: null full "|gpg -e" var=`CMD`
use IO::Plumbing qw(vent plug prng bucket );
# themed import groups!
use IO::Plumbing qw(:tools); # everything so far
DESCRIPTION
IO::Plumbing is a module designed for writing programs which work a bit like shell scripts; where you have data sources, which are fed into pipelines of small programs, connected to make a larger computing machine.
The intention is that the interface behaves much like modules such as IO::All, which is capable of starting threads with external programs. However, the IO::Plumbing object is stackable, and relatively complex arrangements of filehandles and subprocesses are available.
When you plug two or more of these things together, they won't start running commands immediately - that happens the moment you try to read from the output. So, they are lazy.
FUNCTIONS
THE BASIC PLUMBINGS
These functions all return a new IO::Plumbing object with a different configuration.
- plumb(cmdline, arg => value, ...)
-
Shortcut for making a new IO::Plumbing object. Passing in a
cmdline
with a space indicates that you want shell de-quoting. - prng()
-
Shortcut for
/dev/urandom
or other such locally available source of relatively entropic bit sequences.When written to, creates a gpg instance that encrypts to the default recipient.
- plug()
-
When read from, always returns end of file, like /dev/null on Unix.
When written to, always returns an error, /dev/full on Unix or a closed filehandle.
- bucket( [ $contents ] )
-
A small (= in-core) spool of data. Returns end of file when the data has been sent. Specifying the contents is enough to do this.
When written to, fills with data as the process writes. In that case, the contents will normally be a pointer to an array or scalar to fill with input records.
Now, the thing about all of this is that you can only be pouring into one bucket at a time as the parent process is responsible for this. So, remember to only use one bucket at a time until that's all sorted out.
- vent( [ $generator ] )
-
When read from, returns a stream of zeros (by default - or supply
$generator
), like /dev/zero on Unix.When written to, happily consumes any amount of data without returning an error, like /dev/null on Unix.
METHODS
Many of these methods are object properties.
- cwd( $path )
-
Specify a directory to change to after
fork()
time. Honoured for code reference blocks, too. Defaults toundef
, which does not alter the working directory. - env( [ { KEY => VALUE, ... } ])
-
Specify the process environment to use in the child. Defaults to
undef
, which does not alter the environment. - program( [ $path ] )
-
Specify the program to execute.
- args( [ @command ] )
-
Specify a list of arguments to the command. ie, what gets passed to @ARGV in the child. Can be a list of strings or an ArrayRef.
- all_args()
-
primarily of interest to those sub-classing the module, this lets you return something other than what "args" was set to when it comes time to execute.
- cmdline("xxx")
-
As a shortcut to specifying program and args, specify a command line. No shell redirection is yet supported, only basic de-quoting.
- code( sub { ... } )
-
Specify a piece of code to run, instead of executing a program. when the block is finished the child process will call exit(0).
If both code and an external program are passed, then the code block will be run. It receives the IO::Plumbing object as its first argument and the command line arguments after that.
- input( [ $source] [, $weakref ] )
-
Specify the input source of this command pipe. Defaults to a plug.
If you pass a filehandle in, you might also like to call
->close_on_exec($source)
on it to mark it to close when the pipeline executes.If you pass in another IO::Plumbing object (or something which quacks like one), then that object's
output
property is automatically set to point back at this object. So, anIO::Plumbing
chain is a doubly-linked list. The$weakref
flag indicates this is what is happening, and aims to stop these circular references causing memory leaks. - output( [ $dest] [, $weakref ] )
-
Specify the output this command pipe. Defaults to a bucket.
Pass in "|cmdname" as a string for a quick way to make more plumbing.
- stderr( [ $dest] [, $weakref ] )
-
Specify where stderr of this stage goes. Defaults to
STDERR
of the current process. - inputs_include($item)
-
Returns true if the input is the same as
$item
. Sub-classes with multiple inputs should return true if any of their inputs match$item
. - outputs_include($item)
-
Returns true if any output is the same as
$item
. - terminus()
-
Returns the last output object on the "output" chain of this pipeline. Frequently a bucket.
- status()
-
Returns the current status of this piece of plumbing;
Value Meaning -------------------------------------------------- COMMAND_ERROR Not good enough to exec() yet COMMAND_READY Got everything we need to run COMMAND_RUNNING In progress COMMAND_DONE Reaped COMMAND_LOST Process went AWOL
- ready()
- running()
- done()
-
Aliases for checking whether the status is one of them
- status_name()
-
Returns a description of the current status of the process
- pid()
-
Returns the process ID of the running (or completed) process.
- rc()
-
Returns the current return code of the process (ie, what
$?
was set to). If undefined, the program hasn't finished (or isn't started yet); - ok()
-
Returns true if the program exited cleanly.
- error()
-
Returns a true value if the process returned an error code. Includes in the message whether the program exited cleanly, exited with an error code (and if so what the error code was), as well as whether it was killed by a signal (and what the signal was).
- errormsg()
-
Just like error, except guaranteed to never produce a "use of uninitialised variable" warning by returning "finished normally" if the process ran successfully.
- wait()
-
Waits for this specific piece of plumbing to finish.
- name
-
Returns (or sets) a string descriptor for this piece of plumbing.
Available as the overloaded '""' (stringify) operator.
- out_fh( [ $fh ] [ , $close_on_exec ] )
-
specify (or return) the filehandle that will become this child process' STDOUT
- err_fh( [ $fh ] )
-
specify (or return) the filehandle that will become this child process' STDERR
- in_fh( [ $fh ] [ , $close_on_exec ] )
-
specify (or return) the filehandle that will become this child process' STDIN.
- execute()
-
starts this pipeline. Any link can be the starting point for an execute()
- close_on_exec($fh [, $fh, ...])
-
Mark a filehandle that should be closed in the parent process when the pipeline is executed. Note that this is quite a different concept to the OS-level close on exec, which is hinted about at "$^F" in perlvar, which applies to filehandles which are closed in the child process. IO::Plumbing does not alter
$^F
.If you are passing raw filehandles in, the module can't guess whether this filehandle is one that should be closed on execution of the pipeline, or whether it's one that as a parent process you intend to feed or read yourself.
With a normal file, that's not a huge problem - just a wasted FD in the parent process. With the input half of a pipe, it means that the other end will not see the filehandle closed when a sub-process closes it, and hence your pipeline will block as the next program waits forever for an end of file.
So long as you always pass IO::Plumbing objects to the
input
andoutput
methods, you don't need to use this function; when those are converted from objects to filehandles, the temporary filehandles are always marked close on exec.
CLASS METHODS
These may also be called as object methods
- IO::Plumbing->new( $att => $value, [ ... ] )
-
The constructor is very basic, it just calls bare accessors based on
$att
and$value
and then callsBUILD
. - IO::Plumbing->reap( [ $max ] )
-
check for any waiting children and update the RC values of all running plumbing objects, without ever blocking.
$max
specifies the maximum number of children to reap at one time.
SUB-CLASS API
OVERRIDABLE METHODS
- default_input
-
What to use as a default standard input when nothing else is given. Defaults to a IO::Plumbing::Plug (
/dev/null
). Override this in a sub-class to change this behaviour. - default_output
-
What to use as a default standard output. Defaults to a IO::Plumbing::Bucket (ie, a variable buffer).
- default_stderr
-
Default standard error. Defaults to the calling process'
STDERR
. - needs_fork
-
Set this to return a true value if this piece of plumbing needs to fork; false otherwise.
- do_fork
-
A hook for forking
- prefer_code
-
This is another way to specify the code vs program behaviour of the plumbing; it is used by the default execute() function to decide whether to invoke an external program, or use the supplied code block, if both are provided.
The default is to prefer code on Windows.
DEBUGGING
To get debug information to STDERR about forking and plumbing, set IO_PLUMBING_DEBUG
in the environment to 1.
To get further information useful for debugging the IO::Plumbing module, set it to 2 or higher.
AUTHOR AND LICENCE
Copyright 2007, Sam Vilain. All Rights Reserved. This program is free software; you can use it and/or modify it under the same terms as Perl itself.
BUG REPORTS / SUBMISSIONS
If you would like your issue tracked, please submit bug reports and/or patch submissions to:
https://rt.cpan.org/Ticket/Create.html?Queue=IO%3A%3APlumbing
If you want a feature or a bug fixed please at least try to write a test script.
IO::Plumbing
source history is browsable at:
http://utsl.gen.nz/gitweb/?p=IO-Plumbing
From there you will find links to the git source.
If you hate bug trackers with a vengeance, then feel free to just mail me a git-format-patch
-style patch at samv@cpan.org and I'll either ack your patch or nak it with a reason.