NAME
PerlX::bash - tighter integration between Perl and bash
VERSION
This document describes version 0.04_01 of PerlX::bash.
SYNOPSIS
# put all instances of Firefox to sleep
foreach (bash \lines => pgrep => 'firefox')
{
bash kill => -STOP => $_ or die("can't spawn `kill`!");
}
# count lines in $file
my $num_lines;
local $@;
eval { $num_lines = bash \string => -e => wc => -l => $file };
die("can't spawn `wc`!") if $@;
# can capture actual exit status
my $pattern = qr/.../;
my $status = bash grep => -e => $pattern => $file, ">$tmpfile";
die("`grep` had an error!") if $status == 2;
DESCRIPTION
There is one primary function, which is always exported: bash
. This takes several arguments and passes them to your system's bash
command (therefore, if your system has no bash
--e.g. Windows--this module is useless to you). Since bash
is a shell, it will run its arguments as a command, meaning that bash
is functionally very similar to system
. The primary advantages of bash
over system
are:
Actual bash syntax. The
system
command runssh
, and, even ifsh
on your system is just a symlink tobash
, it will not respect the full bash syntax. For instance, thissystem("diff <(sort $file1) <(sort $file2)");
will not work on your system (unless your system is super-special in some magical way), because this type of advanced bash syntax is backwards-incompatible with old Bourne shell syntax. However, this
bash diff => "<(sort $file1)", "<(sort $file2)";
works just fine.
Better return context. The return value of
system
is "backwards" because it returns the exit code of the command it ran, which is 0 if there were no errors, which is false, thus leading to confusing code like so:if (!system($cmd)) { say "It worked!"; }
But
bash
returns true if the command succeeded, and false if it didn't ... in a boolean context. In other scalar contexts, it returns the numeric value of the exit code. If anything goes wrong, an exception is thrown, which can be handy if you're using the return value for something else (like capturing).More capturing options. To capture the output of
system
, you would normally use backquotes, which returns everything as a string. With PerlX::bash, you can capture output as a string, as an array of lines, or as an array of words. See "Run Modes".Better quoting. With
system
, you either pass your arguments as separate arguments, in which case the shell is bypassed, or you pass them as one big string. This can make quoting challenging. With PerlX::bash, you never want to bypassbash
(if you do, you should be usingsystem
instead). Thus, you can specify arguments separately and have things automatically quoted properly (hopefully) without you having to think about it too hard. See "Arguments". Of course, if you'd rather pass the whole command as one big string, you can do that too (see "Switches").Access to (certain) bash switches. Some options to
bash
come in handy. The most important one is probably-e
. Withsystem
, you can eitheruse autodie ':all'
, or not. If you do, then all your commands throw an exception if they don't return success; if you don't, then none of them do. With PerlX::bash, you can just provide-e
(or not) to individual commands to achieve the same effect on a more granular level. Other important switches include-c
and-x
.
Run Modes
You can specify what you want done with the output of bash
via several features collectively called "run modes." If you don't specify any run mode at all (which I sometimes call "just run it!" mode), then output goes wherever it would normally go: probably to your terminal, unless you've redirected it in the bash
command itself.
Run modes are incompatible with each other, whether they're of the same type (e.g. two different capture modes) or different types (e.g. one capture mode and one filter mode). Specifying more than one run mode is a fatal error.
Capture Modes
Capture modes take the ouptut of the bash command and return it for storage into a Perl variable. There are 3 basic capture modes, all of which are indicated by a backslashed argument.
String
To capture the entire output as one scalar string, use \string
, like so:
my $num_lines = bash \string => wc => -l => $file;
This is almost exactly like backquotes, except that the output is chomped for you.
Lines
To capture the output as a series of lines, use \lines
instead:
my @lines = bash \lines => git => log => qw< --oneline >, $file;
Individual lines are pre-chomped.
Words
If you'd rather have the output split on whitespace, try \words
:
my @words = bash \words => awk => '$1 == "foo" { print $3, $5 }', $file;
Specifically, the output is split on the equivalent of /[$ENV{IFS}]+/
; if $IFS
is not set in your environment, a default value of " \t\n"
is used.
Context
\string
always returns a scalar. \lines
and \words
should generally be called in list context; in scalar context, they just return the first element of the list.
Filter Modes
Not yet documented.
Arguments
No matter how many arguments you pass to bash
, they will be turned into a single command string and run via bash -c
. However, PerlX::bash tries to make intelligent guesses as to which of your arguments are meant to be treated as a single argument in the command line (and therefore might require quoting), and which aren't. Understanding what the rules behind these guesses can help avoid surprises.
Basically, there are 3 rules:
Some things are always quoted. See "Autoquoting".
Some things are never quoted. Any argument that begins with a special character (see "Special Characters") is never quoted.
Some things are sometimes quoted. Any argument that contains a special character (see "Special Characters") is quoted.
If an argument falls into multiple categories, the first matching category (according to the order above) wins. Thus, a filename object (which is always quoted) that begins with a special character (meaning it would never be quoted) is quoted. An argument that both begins with a special character (never quoted) and contains a special character later in its string (quoted) is not quoted.
The reason that arguments which begin with a special character are treated differently (oppositely, even) from other arguments containing special characters is to avoid quoting things such as redirections. So, for instance:
bash echo => "foo", ">bar";
is the equivalent of:
system(q| bash -c 'echo foo >bar' |);
whereas:
bash echo => "foo", "ba>r";
is the equivalent of:
system(q| bash -c 'echo foo "ba>r"' |);
Mostly this does what you want. For when it doesn't, see "Quoting Details".
Autoquoting
An autoquoting rule is a reference to a sub
that takes a single argument and returns true or false. Autoquoting rules are tried, one a time, until one of them returns true, at which point the argument is quoted. If none of them return true, autoquoting does not apply.
PerlX::bash starts with a short list of autoquoting rules:
A reference to a regex is stringified and quoted.
Any blessed object whose class has a
basename
method is considered to be a filename and quoted. This covers Path::Class, Path::Tiny, Path::Class::Tiny, and probably many others.
You can also add your own autoquoting rules (feature not yet implemented).
Special Characters
For purposes of determining whether to quote arguments, the most important characteristic is whether a string contains any special characters. Here's the character class of all characters considered "special" by bash:
[ \$'"\\#\[\]!<>|;{}()~]
Note that space is a special character, as are both types of quotes and all four types of brackets, and backslash.
Quoting Details
If an argument is quoted, it is run through "shq", which means it is surrounded with single quotes, and any internal single quotes are appropriately escaped. This is similar to how `bash -x` does it when it prints command lines.
If an argument is not quoted but you wish it were, you can simply call shq
yourself (feature not yet implemented):
bash echo => shq(">bar"); # to print ">bar"
If an argument is quoted but you wish it weren't, you need to fall back to passing the entire command as one big string. For this, use the -c
switch:
bash -c => "echo foo;echo bar";
# or just, you know, make the semi-colon a separate arg:
bash echo => "foo", ';', echo => "bar";
Switches
Most single character switches are passed through to the spawned bash command, but some are handled by PerlX::bash directly.
-c
Just as with system bash, the -c
switch means that the entire command will be sent as one big string. This completely disables all argument quoting (see "Arguments").
-e
Without the use of -e
, any exit value from the command is considered acceptable. (Exceptions are still raised if the command fails to launch or is killed by a signal.) By using -e
, exit values other than 0 cause exceptions.
bash diff => $file1, $file2; # just print diffs, if any
bash -e => diff => $file1, $file2; # if there are diffs, print them, then throw exception
This mimics the bash -e
behavior of the system bash
.
STATUS
This module is still an experiment, but I am currently using it daily for small tasks. The basic functionality is very useful; however, I still cannot yet promise I won't make sweeping changes to the interface. I still welcome suggestions and contributions, and continue to recommend that you do not rely on this in production code (yet).
Documentation is much improved, but still not complete.
SUPPORT
Perldoc
You can find documentation for this module with the perldoc command.
perldoc PerlX::bash
Bugs / Feature Requests
This module is on GitHub. Feel free to fork and submit patches. Please note that I develop
via TDD (Test-Driven Development), so a patch that includes a failing test is much more
likely to get accepted (or least likely to get accepted more quickly).
If you just want to report a problem or suggest a feature, that's okay too. You can create
an issue on GitHub here: L<http://github.com/barefootcoder/perlx-bash/issues>.
Source Code
none https://github.com/barefootcoder/perlx-bash
git clone https://github.com/barefootcoder/perlx-bash.git
AUTHOR
Buddy Burden <barefootcoder@gmail.com>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2015-2019 by Buddy Burden.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)