I am having problems running commands using IPC::run with special characters, in my current case ' and *
The used commands:
apt-get -y --allow-unauthenticated -o Dpkg::lock::timeout=0 install /tmp/archive-keyring*.deb
(the file /tmp/archive-keyring_2022.04.01~tux_all.deb exists)
and
dpkg-query -f '${db:Status-Abbrev} ${Package} ${Version}\n' -W 'archive-keyring'
work without problems when used on a CLI with sudo
. Of course this script has to be started as sudo
as well. If it helps, I am using an Ubuntu system.
The module I am using is libipc-run-perl version 20231003.0-1
Here the example script:
#!/usr/bin/perl -w
use strict qw(vars subs);
use warnings;
use IPC::Run qw( run timeout );
use Data::Dumper;
# for debugging
$ENV{IPCRUNDEBUG} = 'data';
sub startProgram {
my ($subOutput, $subErrors, $subTimeout, $subReturnValue, @subCommand) = @_;
# for debug
print Dumper(@subCommand);
my $runExitCode = q{};
eval {
# undef has to be \undef !!
$runExitCode = run \@subCommand, \undef, $subOutput, $subErrors, timeout ( $subTimeout, exception=>'timeout' );
};
if ($@ =~ /timeout/) {
$subReturnValue = exitCode();
print "Timed out...\n";
} else {
$subReturnValue = exitCode();
print "Completed task without timeout\n";
}
print "output: >$$subOutput<\n".
"errors: >$$subErrors<\n".
"return value: >$subReturnValue<\n".
"run exit code: >$runExitCode<\n\n";
return ($runExitCode);
}
sub exitCode {
return ($?>>8);
}
my $output = q{};
my $errors = q{};
my $timeout = 5;
my $retVal = q{};
# does not work
# problem is: *
# actual command on cmdline:
# apt-get -y --allow-unauthenticated -o Dpkg::lock::timeout=0 install /tmp/archive-keyring*.deb
my @command1 = (
'apt-get',
'-y',
'--allow-unauthenticated',
'-o',
'Dpkg::lock::timeout=0',
'install',
'/tmp/archive-keyring*.deb');
startProgram(\$output, \$errors, $timeout, \$retVal, @command1);
# does not work
my @command2 = (
'apt-get',
'-y',
'--allow-unauthenticated',
'-o',
'Dpkg::lock::timeout=0',
'install',
'/tmp/archive-keyring\*.deb');
startProgram(\$output, \$errors, $timeout, \$retVal, @command2);
# does not work
my $fileName = q{/tmp/archive-keyring*.deb};
my @command3 = (
'apt-get',
'-y',
'--allow-unauthenticated',
'-o',
'Dpkg::lock::timeout=0',
'install',
$fileName);
startProgram(\$output, \$errors, $timeout, \$retVal, @command3);
# this works !! but I have to use * for different reasons
# I am using here the full filename of the package
my @command4 = (
'apt-get',
'-y',
'--allow-unauthenticated',
'-o',
'Dpkg::lock::timeout=0',
'install',
'/tmp/archive-keyring_2022.04.01~tux_all.deb');
startProgram(\$output, \$errors, $timeout, \$retVal, @command4);
# Another command I need and is not working:
# dpkg-query -f '${db:Status-Abbrev} ${Package} ${Version}\n' -W 'archive-keyring'
# here the problem seems to be: '
# IPC::run seems to convert ' into ''
# does not work
my @command5 = (
'dpkg-query',
'-f',
'\'${db:Status-Abbrev} ${Package} ${Version}\n\'',
'-W',
'\'archive-keyring\'');
startProgram(\$output, \$errors, $timeout, \$retVal, @command5);
# does not work
my @command6 = (
"dpkg-query",
"-f",
"'\${db:Status-Abbrev} \${Package} \${Version}\n'",
"-W",
"'archive-keyring'");
startProgram(\$output, \$errors, $timeout, \$retVal, @command6);
# does not work
my $package = 'archive-keyring';
my @command7 = (
"dpkg-query",
"-f",
"'\${db:Status-Abbrev} \${Package} \${Version}\n'",
"-W",
$package);
startProgram(\$output, \$errors, $timeout, \$retVal, @command7);
# does not work
# same as before but completely separated as array
my @command8 = (
'dpkg-query',
'-f',
'\'${db:Status-Abbrev}',
'${Package}',
'${Version}\n\'',
'-W',
'\'archive-keyring\'');
startProgram(\$output, \$errors, $timeout, \$retVal, @command8);
# does not work
my @command9 = (q{dpkg-query}, q{-f}, q{'${db:Status-Abbrev} ${Package} ${Version}\n'}, q{-W}, q{'archive-keyring'});
startProgram(\$output, \$errors, $timeout, \$retVal, @command9);
exit (0);
Somehow I am having problems escaping the characters * and '. It seems like IPC::run itself is escaping the characters as well rendering the commands useless.
What am I doing wrong?
How do I escape correctly?
Some of the debug info I get are:
execing /usr/bin/apt-get -y --allow-unauthenticated -o Dpkg::lock::timeout=0 install /tmp/archive-keyring*.deb
errors: >E: Unsupported file /tmp/archive-keyring*.deb given on commandline
execing /usr/bin/dpkg-query -f ''${db:Status-Abbrev} ${Package} ${Version}\n'' -W 'archive-keyring'
errors: >dpkg-query: no packages found matching 'archive-keyring'
IPC::Run 0001 [#6(14085) dpkg-query]: execing /usr/bin/dpkg-query -f ''${db:Status-Abbrev} ${Package} ${Version}
IPC::Run 0001 [#6(14085) dpkg-query]: '' -W 'archive-keyring'
errors: >dpkg-query: no packages found matching 'archive-keyring'
IPC::Run 0001 [#7(14086) dpkg-query]: execing /usr/bin/dpkg-query -f ''${db:Status-Abbrev} ${Package} ${Version}
IPC::Run 0001 [#7(14086) dpkg-query]: '' -W archive-keyring
errors: >dpkg-query: no packages found matching archive-keyring
execing /usr/bin/dpkg-query -f '${db:Status-Abbrev} ${Package} ${Version}\n' -W 'archive-keyring'
errors: >dpkg-query: error: need an action option
As I said, the commands work perfectly when entered into a terminal.
The following is a shell command:
dpkg-query -f '${db:Status-Abbrev} ${Package} ${Version}\n' -W 'archive-keyring'
To evaluate it, the shell executes dpkg-query
with the following arguments:
dpkg-query
-f
${db:Status-Abbrev} ${Package} ${Version}\n
-W
archive-keyring
Note the lack of quotes.
To pass these arguments using run
, one could use the following:
my @cmd = (
"dpkg-query" => (
"-f" => '${db:Status-Abbrev} ${Package} ${Version}\n',
"-W" => "archive-keyring",
)
);
The following is a shell command:
apt-get -y --allow-unauthenticated -o Dpkg::lock::timeout=0 install /tmp/archive-keyring*.deb
To evaluate it, the shell executes apt-get
with the following arguments:
apt-get
-y
--allow-unauthenticated
-o
Dpkg::lock::timeout=0
install
/tmp/archive-keyring_2022.04.01~tux_all.deb
Note that /tmp/archive-keyring*.deb
was expanded by the shell.
If you're going to avoid the shell, you're going to need to replicate the behaviour of the shell.
my @cmd = (
"apt-get" => (
"-y",
"--allow-unauthenticated",
"-o" => "Dpkg::lock::timeout=0",
"install" => glob( "/tmp/archive-keyring*.deb" ),
)
);
The following function shows the arguments the shell passes to it:
show-args() { perl -Mv5.14 -e'say 0+@ARGV; say ">$_<" for @ARGV' -- "$@"; }
We can examine your commands using it.
show-args dpkg-query -f '${db:Status-Abbrev} ${Package} ${Version}\n' -W 'archive-keyring'
5
>dpkg-query<
>-f<
>${db:Status-Abbrev} ${Package} ${Version}\n<
>-W<
>archive-keyring<
show-args apt-get -y --allow-unauthenticated -o Dpkg::lock::timeout=0 install /tmp/archive-keyring*.deb
7
>apt-get<
>-y<
>--allow-unauthenticated<
>-o<
>Dpkg::lock::timeout=0<
>install<
>/tmp/archive-keyring_2022.04.01~tux_all.deb<