1

I have a Perl script progA.pl which needs to run another Perl script progB.pl using the system command. However, progB.pl has been aliased in ~/.bashrc so I need to ensure that it is run after ~/.bashrc has been loaded. I can achieve this by using bash with the -lc option.

For this question, I simplify the problem as much as I think is needed, by considering the following version of progB.pl

use feature qw(say);
use strict;
use warnings;
use Data::Dump qw(dd dump);

say "Received \@ARGV: " . dump @ARGV;

and here is progA.pl:

use feature qw(say);
use strict;
use warnings;

use Data::Dump qw(dd dump);

my $cmd = qq(progB.pl --opt='This option contains '"'"'single'"'"' quotes');
say "cmd = " . dump($cmd);
system( "$cmd" );
say "-----";
system( 'bash -c ' . "$cmd" );
say "-----";
system( 'bash -c ' . "'$cmd'" );
say "-----";
system( "bash -c  \"$cmd\"" );

Running

$ progA.pl

gives output:

cmd = "progB.pl --opt='This option contains '\"'\"'single'\"'\"' quotes'"   
Received @ARGV: "--opt=This option contains 'single' quotes"  
-----   
Received @ARGV: ()  
-----   
Received @ARGV: "--opt=This"  
-----   
Received @ARGV: "--opt=This option contains single quotes"

We see that this works fine, when progB.pl is run directly without using bash -c. When I use bash -c to run the command, none of the three alternatives are working correctly.

How can I run progB.pl with an argument containing single quotes and at the same time using using bash -c ?

Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • 2
    One of the reasons that perl was created was as a reaction to the multiple recursive bash calls, and to the quotation hell that you are experiencing. If it is an option, I would recommend rewriting your bash scripts as perl modules... – Dov Grobgeld Mar 20 '15 at 08:31
  • @DovGrobgeld Good advice, but I think it is not an option for this case.. – Håkon Hægland Mar 20 '15 at 08:36

2 Answers2

2

You should avoid this quoting madness at first place but if you insist, you should avoid at least one level of quoting by using system ARRAY version.

my $cmd = q{progB.pl --opt='This option contains '"'"'single'"'"' quotes'};
system( qw(bash -c), $cmd );

It makes it only one level of quoting madness.

my $option = q{This option contains 'single' quotes} =~ s/'/'"'"'/gr; # '
my $cmd = qq{progB.pl --opt='$option'};
system( qw(bash -c), $cmd );

There you can make some simple helper

sub sq ($) { "'" . $_[0] =~ s/'/'"'"'/gr . "'" } # "

my $option = q{This option contains 'single' quotes};
my $cmd = qq{progB.pl --opt=@{[sq $option]}};
system( qw(bash -c), $cmd );
Hynek -Pichi- Vychodil
  • 26,174
  • 5
  • 52
  • 73
0

After some trial and error, I arrived at:

use feature qw(say);
use strict;
use warnings;

my $cmd = qq(print_first_arg.pl --opt='This option contains '"'"'single'"'"' quotes');
$cmd =~ s/'/'"'"'/g;
system( 'bash -c ' . "'$cmd'" );

It seems to work, for this test case at least..

This also follows the approach suggested by @ysth in this answer: https://stackoverflow.com/a/24869016/2173773

Community
  • 1
  • 1
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174