How to escape ',' sign at command line? - perl

When I do in perl script (source):
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack\,\#DB::goto_frames]' -e '1'`
The module gets two arguments:
s;e [\#DB::goto_frames\
\#DB::stack];
But I want to get only one:
s;e [\#DB::goto_frames,\#DB::stack];
How to escape ',' sign?

It's got to be your module splitting on the comma, not perl or the shell. Running the following under bash, I get only a single argument in #ARGV:
$ perl -w -E 'say join "\n", ("---", #ARGV, "---")' 's;e [\#DB::stack\,\#DB::goto_frames]'
---
s;e [\#DB::stack\,\#DB::goto_frames]
---
Edit:
I stand corrected. It is being split on commas by perl, presumably to allow passing multiple arguments to a module, as I proved to myself by creating a module at ./Devel/DbInteract.pm containing:
package Devel::DbInteract;
use strict;
use warnings;
use 5.010;
sub import {
say 'Received ' . scalar #_ . ' params:';
say join "\n", #_;
}
1;
and running the command:
$ PERL5LIB=. perl -d:DbInteract='s;e [\#DB::stack,\#DB::goto_frames]' -e ''
Received 3 params:
Devel::DbInteract
s;e [\#DB::stack
\#DB::goto_frames]
Judging by the source linked in the asker's answer, there does not appear to be any provision for escaping values or any other way to prevent this splitting. Your options, then, would be to either work around it by joining the params back together or submitting a patch to the perl source to add an allowance for escaping.

Perl does not care about escaping: https://github.com/Perl/perl5/blob/blead/perl.c#L3240-L3264
the -d flag just add next as zero line number into the script:
use Devel::DbInteract split(/,/,q{s;e [\#DB::stack\,\#DB::goto_frames]});

Some people advice me simple approach than patching perl. Just to use => in my case:
`perl -d:DebugHooks::DbInteract='s;e [\#DB::stack => \#DB::goto_frames]' -e '1'`

Related

How to handle quotes in Perl when calling system commands?

I'm trying to make a very simple script to call the PDFXchange viewer from a .desktop file. But I'm not able to do this because the bash is seeing things that I'm not aware:
#!/usr/bin/perl
use strict;
use warnings;
my $winepath = `winepath -w -0 "$ARGV[0]"`;
my $cmd=join ' ',
'wine',
'\'C:\Program Files\Tracker Software\PDF Viewer\PDFXCview.exe\'',
"\'$winepath\'";
print $cmd . "\\n";
exec $cmd;
Output
$ exec_pdfxcv 'pdf with spaces.pdf'
wine 'C:\Program Files\Tracker Software\PDF Viewer\PDFXCview.exe' 'Z:\media\FILES\pdf with spaces.pdf'\nsh: 1: Syntax error: Unterminated quoted string
I'm very beginner at Perl, so I run out of ideas very quickly after tried a couple of times with different quotes configurations.
It seems that you dodged the bullet (of the common inscrutable mangle of quotes and escapes) here, since you don't need the shell for what is done and the rest doesn't need much either.
Then use the list-form of exec, which bypasses the shell altogether. This can be done with system as well but, alas, not with backticks.
my $winepath = `winepath -w -0 "$ARGV[0]"`;
chomp $winepath;
my #cmd = (
'wine',
q('C:\Program Files\Tracker Software\PDF Viewer\PDFXCview.exe'),
"'$winepath'"
);
exec #cmd;
Note the use of the q() operator as single quotes, freeing the symbol ' for use inside.
We should remove the newline from $winepath (returned by qx) and need to protect spaces inside it by adding ' around it.
Going through the shell only makes it harder; do it only when you specifically need the shell.
Once you do need the shell, however, be aware of String::ShellQuote and Win32::ShellQuote.
Finally, work through Quoting the Shell from Perl.com. A required reading in my opinion.
I finally make it work after a hundred of tries.
#!/usr/bin/perl
use strict;
use warnings;
my $winepath = `winepath -w -0 "$ARGV[0]"`;
exec 'wine', q(C:\Program Files\Tracker Software\PDF Viewer\PDFXCview.exe), "".$winepath."";
I hope help someone!
If there is anyone interested in the .desktop file:
[Desktop Entry]
Name=PDF XChange Viwer
Comment=View multi-page documents
Exec=exec_pdfxcv %U
StartupNotify=true
Terminal=false
Type=Application
StartupWMClass=PDFXCview.exe
Icon=evince
Categories=GNOME;GTK;Office;Viewer;
MimeType=application/pdf;application/x-bzpdf;application/x-gzpdf;application/x-xzpdf;application/x-ext-pdf;application/postscript;application/x-bzpostscript;application/x-gzpostscript;image/x-eps;image/x-bzeps;image/x-gzeps;application/x-ext-ps;application/x-ext-eps;application/x-dvi;application/x-bzdvi;application/x-gzdvi;application/x-ext-dvi;image/vnd.djvu;image/vnd.djvu+multipage;application/x-ext-djv;application/x-ext-djvu;image/tiff;application/x-cbr;application/x-cbz;application/x-cb7;application/x-cbt;application/x-ext-cbr;application/x-ext-cbz;application/vnd.comicbook+zip;application/x-ext-cb7;application/x-ext-cbt;application/oxps;application/vnd.ms-xpsdocument;

Awk not working inside a perl scipt?

I am trying to execute below awk command inside a perl script, but it is failing.
#!/usr/bin/perl
print `awk -F, '{print $NF}' f1.txt > f2.txt`
This is the error:
syntax error at ./MO.pl line 3, near "print"
Execution of ./MO.pl aborted due to compilation errors.
Can anyone please help what I am doing wrong here?
This is a Perl error and has nothing to do with your awk script itself. The error is usually seen when the previous statement doesn't have a semicolon at the end.
Here's a very simple program (which should include use strict; and use warnings;, but I wanted to emulate what you have).
#! /usr/bin/env perl
#
print "Hello, World\n" # Line 4
print "Hello, Back\n"; # Line 5
And the error message is:
syntax error at test.pl line 5, near "print"
Execution of test.pl aborted due to compilation errors.
Note the error is near the print in Line #5, but the error is actually at the end of Line #4 where I'm missing a semicolon.
Running your exact program works on my system (although doesn't quite produce the results you want). I am assuming this isn't your exact program, but instead a simplification of your program. Is there a statement before that print?
Several other things:
You're redirecting your awk output, so there's nothing to print.
Use strict and warnings.
Better to use qx(....) than backticks (grave accent). It's more readable and allows you to do quoted executable in quoted executable.
Watch for Perlisms in your code. The $NF is interpreted by Perl, and without the use strict;, doesn't give you an error. Instead, the print in your Awk statement is a null print which prints the entire line.
Why do you use print if nothing is printing out? You're better off in this position to use system which allows you to put single quotes around your entire statement:
system q(awk -F, '{print $NF}' f1.txt > f2.txt);
This way, $NF doesn't have to be quoted.
Why are you doing Awk in a Perl program? Perl will do anything Awk will do and do it better:
Here's a version of your program using plain ol' Perl:
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
while ( my $line = <> ) {
my #array = split /\s*,\s*/, $line;
print $array[-1];
}
To run this program:
$ test.pl f1.txt > f2.txt
Yes, it's longer, but half of the program is taken up by pragmas that all Perl programs should use.
I'm sure people smarter than me can turn this into a Perl one-liner.
Since you're redirecting the awk output, there's nothing for perl to print. You might as well use system and the quoting operator q():
system q(awk -F, '{print $NF}' f1.txt > f2.txt)
Or, of course, do it in perl, which saves you from having to spawn a shell and then spawn awk:
open my $in, '<', 'f1.txt';
open my $out, '>', 'f1.txt';
while (<$in>) {
print $out (split " ")[-1], "\n";
}
close $in;
close $out;
If there are more lines in the script, you need a semi-colon at the end of the print statement.

Weird getopt =s handling?

code:
#!/usr/bin/env perl
use strict;
use warnings;
use locale;
my $prepinac_r = '';
my $array_name = '';
use Getopt::Long;
Getopt::Long::Configure ("bundling");
my $result = GetOptions(
"r=s" => \$prepinac_r,
"array-name=s" => \$array_name);
print STDERR "r: $prepinac_r\n";
print STDERR "array_name: $array_name\n";
running it:
script.pl --array-name=kokos -r=kure
output:
r: =kure
array_name: kokos
What I am doing wrong? What did I miss? Why -r gets "=kure" instead of "kure" ? Please help...
You're mixing short for and long form syntax. Short form syntax doesn't use = since that would make it less short.
"a|all" => \$opt_all,
"e|execute=s" => \$opt_execute,
Short form:
-aefoo
-a -efoo
-a -e foo
Long form:
--all --execute=foo
--all --execute foo
Here's an example of the short form you might be familiar with:
perl -le'print "Hello World";'
perl -l -e'print "Hello World";'
perl -l -e 'print "Hello World";'
Mr. Student, you should use double - for both switches >>
script.pl --array-name=coconut --r=chicken
...
r: chicken
array_name: coconut
I believe the behavior you are seeing is because of "bundling".
From Getopt::Long
Enabling this option will allow single-character options to be bundled. To distinguish bundles from long option names, long options must be introduced with -- and bundles with -
So if you are using "bundling", then -
--array-name=foo --r=bar # Works
-afoo -rbar # Also works
--array-name=foo -r=bar # Does not. as you've already seen
It also does not make sense to use bundling unless you are using options that do not require a parameter, and therefor can be "bundled"

How do I use perl like sed?

I have a file that has some entries like
--ERROR--- Failed to execute the command with employee Name="shayam" Age="34"
--Successfully executed the command with employee Name="ram" Age="55"
--ERROR--- Failed to execute the command with employee Name="sam" Age="23"
--ERROR--- Failed to execute the command with employee Name="yam" Age="3"
I have to extract only the Name and Age of those for whom the command execution was failed.
in this case i need to extract shayam 34 sam 23 yam 3. I need to do this in perl. thanks a lot..
perl -p -e 's/../../g' file
Or to inline replace:
perl -pi -e 's/../../g' file
As a one-liner:
perl -lne '/^--ERROR---.*Name="(.*?)" Age="(.*?)"/ && print "$1 $2"' file
Your title makes it not clear. Anyway...
while(<>) {
next if !/^--ERROR/;
/Name="([^"]+)"\s+Age="([^"]+)"/;
print $1, " ", $2, "\n";
}
can do it reading from stdin; of course, you can change the reading loop to anything else and the print with something to populate an hash or whatever according to your needs.
As a one liner, try:
perl -ne 'print "$1 $2\n" if /^--ERROR/ && /Name="(.*?)"\s+Age="(.*?)"/;'
This is a lot like using sed, but with Perl syntax.
The immediate question of "how do I use perl like sed?" is best answered with s2p, the sed to perl converter. Given the command line, "sed $script", simply invoke "s2p $script" to generate a (typically unreadable) perl script that emulates sed for the given set of commands.
Refer to comments :
my #a = <>; # Reading entire file into an array
chomp #a; # Removing extra spaces
#a = grep {/ERROR/} #a; # Removing lines that do not contain ERROR
# mapping with sed-like regexp to keep only names and ages :
#a = map {s/^.*Name=\"([a-z]+)\" Age=\"([0-9]+)\".*$/$1 $2/; $_} #a;
print join " ",#a; # print of array content

Can I run a Perl script from stdin?

Suppose I have a Perl script, namely mytest.pl. Can I run it by something like cat mytest.pl | perl -e?
The reason I want to do this is that I have a encrypted perl script and I can decrypt it in my c program and I want to run it in my c program. I don't want to write the decrypted script back to harddisk due to secruity concerns, so I need to run this perl script on-the-fly, all in memory.
This question has nothing to do with the cat command, I just want to know how to feed perl script to stdin, and let perl interpreter to run it.
perl < mytest.pl
should do the trick in any shell. It invokes perl and feeds the script in via the shell redirection operator <.
As pointed out, though, it seems a little unnecessary. Why not start the script with
#!/usr/bin/perl
or perhaps
#!/usr/bin/env perl
? (modified to reflect your Perl and/or env path)
Note the Useless Use of Cat Award. Whenever I use cat I stop and think whether the shell can provide this functionality for me instead.
Sometimes one needs to execute a perl script and pass it an argument. The STDIN construction perl input_file.txt < script.pl won't work. Using the tip from How to assign a heredoc value to a variable in Bash we overcome this by using a "here-script":
#!/bin/bash
read -r -d '' SCRIPT <<'EOS'
$total = 0;
while (<>) {
chomp;
#line = split "\t";
$total++;
}
print "Total: $total\n";
EOS
perl -e "$SCRIPT" input_file.txt
perl mytest.pl
should be the correct way. Why are you doing the unnecessary?
cat mytest.pl | perl
…is all you need. The -e switch expects the script as a command line argument.
perl will read the program from STDIN if you don't give it any arguments.
So you could theoretically read an encrypted file, decrypt it, and run it, without saving the file anywhere.
Here is a sample program:
#! /usr/bin/perl
use strict;
use warnings;
use 5.10.1;
use Crypt::CBC;
my $encrypted = do {
open my $encrypted_file, '<', 'perl_program.encrypted';
local $/ = undef;
<$encrypted_file>;
};
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(
'-key' => $key,
'-cipher' => 'Blowfish'
);
my $plaintext = $cipher->decrypt($encrypted);
use IPC::Run qw'run';
run [$^X], \$plaintext;
To test this program, I first ran this:
perl -MCrypt::CBC -e'
my $a = qq[print "Hello World\n"];
my $key = pack("H16", "0123456789ABCDEF");
my $cipher = Crypt::CBC->new(-key=>$key,-cipher=>"Blowfish");
my $encrypted = $cipher->encrypt($a);
print $encrypted;
' > perl_program.encrypted
This still won't stop dedicated hackers, but it will prevent most users from looking at the unencrypted program.