How do I wrap this shell command in Perl? - perl

Is there a way to wrap the following linux command into the Perl system function?
date --set="$(ssh richard#192.168.0.4 'date -u')"
I have tried the following but cannot find a combination that works:
use strict;
system("date --set="$(ssh richard#192.168.0.4 'date -u')"");
system, "date", "--set="$(ssh richard#192.168.0.4 'date -u')"";

You can use backticks to run a command through your shell. The backtick is an experssion that evaluates to the standard output of the command you execute.
use strict;
my $remote_date = `ssh richard\#192.168.0.4 'date -u'`;
chomp $remote_date;
system("date --set='$remote_date'");
The variable $remote_date will contain whatever ssh would print on the screen, including, possibly, login messages. The newline programs typically print at the end of every line will also be included, so I threw in a chomp.
This assumes the command ran succesfully. You can check the exit status of a program with the $? variable, but I am not sure, in your case, if this would give you the status of ssh or the remote date command you attempted to execute.

The problem is that you didn't escape the ", $ and # within.
system( "date --set=\"\$( ssh richard\#192.168.0.4 'date -u' )\"" );
In this case, it's cleaner to use single-quotes on the outside, and double-quotes on the inside.
system( 'date --set="$( ssh richard#192.168.0.4 "date -u" )"' );

wrap commands in Perl, with or without variables/special characters:
use strict; use warnings;
my $remote_date = system<<'EOF';
ssh richard#192.168.0.4 'date -u'
EOF
chomp $remote_date;
system<<EOF;
date --set='$remote_date'
EOF
Check perldoc perlop#Quote-and-Quote-like-Operators
Especially the part about 'QuoteHereDocument'

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;

Perl Expect.pm send control character to subprocess

I am trying to write a script that uses the bash line editor to recall a previously entered command. Here's the simple session I'm trying to automate.
$ bash --norc --noprofile
bash4.4$ echo hi
hi
bash4.4$
then type '^P^M'
bash4.4$ echo hi
hi
Here is my first attempt at scripting this using the Expect.pm module off CPAN. The two sleep 1s are in there to guard against the possibility of race conditions when invoking the line editor since I'm not sure what perl sees when that happens.
#!/usr/bin/env perl
use strict;
use warnings FATAL => 'all';
use autodie;
use Expect;
my $timeout = 10;
my $exp = Expect->new();
$exp->spawn('bash --norc --noprofile');
# wait for first prompt
$exp->expect($timeout, '$ ');
# send echo hi
$exp->send("echo hi\n");
# wait for prompt again
$exp->expect($timeout, '$ ');
# use history recall ^P, then send ^M
sleep 1;
$exp->send("\cp\cm");
sleep 1;
print "okay done!\n";
It works up until I hit "\cp\cm" (I've also tried "\cP\cM"). According to the perl documentation, \cX introduces an ASCII control character. (http://perldoc.perl.org/perlrebackslash.html#Character-Escapes)
Control characters \c is used to denote a control character; the
character following \c determines the value of the construct. For
example the value of \cA is chr(1), and the value of \cb is chr(2),
etc. The gory details are in Regexp Quote-Like Operators in perlop. A
complete list of what chr(1), etc. means for ASCII and EBCDIC
platforms is in OPERATOR DIFFERENCES in perlebcdic.
This is what I actually get when I run the script, which strongly suggests that the control characters are not getting passed to the subprocess properly and possibly aren't passed at all.
% perl bash.pl
bash-4.4$ echo hi
hi
bash-4.4$ okay done!
What's going on here? How do I pass a control character to a process with Expect.pm?
The characters escapes you are searching are specific to Perl regex. For your purpose, I would suggest you to pass the hex value for ctrl-M and ctrl-P.
$exp->send("\x10"); # ctrl+P
$exp->send("\x0D"); # ctrl+M
Update(tested):
$exp->send("\x10"); # ctrl+P
$exp->send("\n"); # send newline
sleep 2;
# wait for prompt
$exp->expect($timeout, '$ ');
$exp->send("\x0D"); # ctrl+M
$exp->send("\n"); # send newline

Running a system command in a perl script that has "#" character

I have a system command like this :
unix_command "#output_file path_to_file"
Now when I try exec or system commands in a perl script I get this error :
Getting a string when expecting an operator.
Can you please help me how to do it in Perl.
Appreciate your help.
Thanks a ton!
Rakesh
system is really two different functions.
You can use it to launch a program.
The following syntax are used to launch a program:
system($prog, #one_or_more_args)
system({ $prog }, $arg0, #args)
Using one of these syntax, all strings passed as arguments are passed untouched to the child program.
Example usage:
system('perl', '-e', 'my #a = "foo"; print "#a\n";');
You can use it to execute a shell command.
The following syntax are used to execute a shell command:
system($shell_cmd)
The above is short for
system('/bin/sh', '-c', $shell_cmd)
You must provide a valid shell command. It you are building the command, you will need to take care to properly escape anything that needs escaping.
Example usage:
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote('perl', '-e', 'my #a = "foo"; print "#a\n";');
system($cmd);
A bit more specifically to your case, the shell command
program #file1 file2
can be executed as follows:
system('program', '#'.$file1, $file2);
If you actually need to construct a shell command (e.g. because you want to redirect output), you can use the following:
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote('program', '#'.$file1, $file2) . ' >output.txt 2>&1';
system($cmd);
If you don't need interpolation, use single quotes.
system 'echo #a';
If you do, use backslash.
system "echo \#a";

Why does system call affect subsequent print behaviour in perl?

Here's my code
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my $file = $ARGV[0];
system('wc -l $file');
print "\nprinting alpha \n";
sleep 1;
exit;
After I run (in tcsh shell) perl script.pl /path/to/file I don't see printing alpha until I press Ctrl+C. Even when I add another statement $|=1 either before or after system call, the behaviour remains the same.
What is happening?
You are executing the shell command
wc -l $file
The shell has no variable $file defined, so that's the same as
wc -l
This causes the shell to execute wc with the lone arg -l. With no file name provided, wc in turn reads from STDIN until you kill it with SIGINT from Ctrl-C.
You were perhaps aiming for
system("wc -l $file"); # XXX
but that's wrong too. That doesn't pass the args -l and the value of $file to wc. Consider what would happen if a file name with a space in it was provided.
To build a shell literal that results in the correct file name, you could use
use String::ShellQuote qw( shell_quote );
system(shell_quote('wc', '-l', $file));
But a better option is to avoid the shell and execute wc directly, passing to it the values you want without having to build a shell command.
system('wc', '-l', $file);
Because the single quotes prevent interpolation of $file. Change to double quotes.
What is happening is that the string is being executed without substituting a value for $file. When the shell gets this it looks for a shell variable $file which does not exist, so it executes the command with no file. This causes wc to read from stdin, resulting in the behavior you see.

Use of pipe within backtick command

I'm having an issue with some code and I'm wondering if anyone can assist.
Basically I'm trying to execute an isql query against a database and assign it to a scalar variable. The isql command makes use of the column seperator which is defined as the pipe symbol.
So I have it set-up as such:
my $command = "isql -S -U -s| -i";
my $isql_output = `$command`;
The isql command works in isolation but when issued as a backtick it stops at the pipe. I've tried concatenating the $command string using sub-strings, using single quotes and backslash escaping items such as -s\"\|\" to no avail. I've also tried using qx instead of backticks.
Unfortunately I'm currently using an older version of perl (v5.6.1) with limited scope for upgrade so I'm not sure if I can resolve this.
You have to quote the | in a way that the shell does not recognize it as a special character. Two ways:
Put the -s| into single quotes: '-s|'. Perl will leave single quotes inside double quoted strings alone and pass them to the shell unmodified.
Escape the | with two backslashes: -s\\|. Why two? The first one is seen by Perl telling it to pass the next character through unmodified. Therefore the shell sees -s\| and does something very similar: it sees the single \ and knows not to treat the next char, |, special.
The problem is that the command is being executed through a shell.
You can avoid this by passing the command and arguments in a list rather than a single string.
The backtick construct does not support that, so you would need to use the open() function instead.
I haven't tested the following code but it gives the idea:
my #command = (qw(isql -Sserver -Uuser -Ppassword -s| -w4096), '–i' . $file);
print join(' ', #command), "\n";
open(my $fh, '-|', #command)
or die "failed to run isql command: $#\n";
my #isql_output = <$fh>;
close($fh);
my $isql_output = $isql_output[0]; chomp($isql_output);
If you're working with a 15 year old version of Perl (which Oracle users tend to do) I'm not sure this will all be supported. For instance, you may need to write chop instead of chomp.
UPDATE: the problem is not the Perl version, but this construct not being supported on Windows, according to the documentation. This must be qualified: I use Perl on Cygwin and it works fine there, but I don't know whether you can use Cygwin.
Single quotes should work. Try to run test perl script:
my $cmd = "./test.sh -S -U -s '|' -i";
print `$cmd`;
With test.sh:
#!/bin/sh
echo $#
Output should be -S -U -s | -i