I am rewriting a bash script in Perl.
The original script checks to see whether a Linux package is in an unconfigured state, and deletes and reinstalls it if so.
#!/bin/bash
if [[ $(dpkg -l | grep libc-bin | grep iF) || $(dpkg -l | grep libc-dev-bin | grep iU) ]] ; then
echo "do something"
fi
I started to see if I could use system calls and store them as variables and then just run an if
statement of these multiple variables. This did not seem to work.
#!/usr/bin/perl
my $libcUnconfigured = system("dpkg -l | grep libc-bin | grep iF");
my $libcDevUnconfigured = system("dpkg -l | grep libc-dev-bin | grep iF");
if ( $libcUnconfigured || $libcDevUnconfigured ) {
print "Do something";
}
In order to receive output from an external command use the qx
operator, not system which returns the exit status of the program as returned by wait
.
I suggest to use external programs only for things that you cannot do in Perl or, rarely, when they greatly simplify your work. For all else use Perl's extensive processing capabilities.
In this case filter the return from dpkg -l
by grep
my @libcUnconfigured = grep { /libc-bin|iF/ } qx(dpkg -l);
chomp @libcUnconfigured;
print "Do something with $_\n" for @libUnconfigured;
The qx
returns a list of lines of output when used in list context, here imposed by grep
. The code block in grep
is run on an element at a time where each is available in the default $_
variable; the regex match is by default done on $_
. The items for which the code evaluates to true pass and are returned as a list, here assigned to an array.
Note that qx
uses /bin/sh
, generally relegated to another shell on your system. So put the command together carefully. See linked docs and $?
in perlvar for error checking.
Each line of output in the returned list comes with its newline, which I remove assuming some non-trivial processing with those filenames. (Wouldn't need to chomp
for printing alone of course.)
Alternatively, you can reach for one of a number of modules. A nice one is Capture::Tiny
use warnings;
use strict;
use feature 'say';
use Capture::Tiny qw(capture);
my @cmd = qw(dpkg -l);
my ($stdout, $stderr) = capture {
system (@cmd);
};
warn "Error with @cmd: $stderr" if $stderr;
say "Do something with $_" for (split /\n/, $stdout);
for its clean syntax, the fact that it hands us the error, and for its ability to run nearly any code
Capture::Tiny provides a simple, portable way to capture almost anything sent to STDOUT or STDERR, regardless of whether it comes from Perl, from XS code or from an external program.
Here the command is formed as a list, what allows system
to bypass the shell. This is better, unless you need the shell. The return in this case is a (possibly multiline) string which is thus split to process lines with package information.
Some others, in increasing capability and complexity of use, are IPC::Run3 and IPC::Run
Also see this entry in perlfaq8. Note that IPC::Open3 used in some examples is rather low-level.