How do I make a perl script run another perl script? - perl

I am writing a large Perl script, which needs to utilize other existing Perl scripts. The problem is the main script needs to reference many different scripts from different folders. For example the main script would be contained in:
/perl/programs/io
It may need to run a script which is stored in:
/perl/programs/tools
Note that there are other orthogonal folders besides tools so I need to be able to access any of them on the fly.
Currently this is what I got:
my $mynumber = '../tools/convert.pl bin2dec 1011';
In theory it should move back from the io directory then enter the appropriate tool directory and call the convert.pl script while passing it the parameters.
All this does is store the string in the single quotes to $myNumber.

I like to assign the output of a command to an array so I can loop through the array to find error or other messages. For example if I'm making a zip file to email to someone I want to check to see if the zip program had any errors before I continue to make and send the email.
#msgs = `zip -f myfile.zip *.pl`; # Use backticks
You can also assign the output to a scalar:
$msg = `ls -al *.pl`; # Use backticks

To run any system command or script all you have to do is use `backticks`. From observing another programer's perl code, I misread these strange quotes for 'single quotes'.
backticks are also nice because they return the text in STDOUT to your perl script so that the output can be assigned to a variable, something I have found impossible if using system("");

The similar question answer does not work with my version of perl. The line
use IPC::System::Simple qw(system capture);
throws some errors. However just using system works, like this:
my $mynumber = system($^X, "../tools/convert.pl", 'bin2dec', '1011');
I can use the above without setting equal to something to execute scripts which return no value and are only sent arguments.
This seems to be the easiest way to do what I need to and the entire programs folder can be moved anywhere and it will still work as no parent directories above programs are used.

Related

How can I get perl to correctly pass a command line argument with multiple arguments and complex file paths (spaces and symbols)?

I have a small perl script which collects file paths from an excel file and passes them through the command line to perltex which then compiles a pdf based on the files and paths chosen.
My problem is that the moment I introduce more complex file paths (which is necessary based on the network setup of the final user pool) perltex fails to find the file paths, cutting them at the space.
A MWE is a follows
#!/usr/bin/perl
use strict;
use warnings;
use 5.14.2;
use Text::Template;
use Spreadsheet::Read;
use Spreadsheet::ParseXLSX;
use utf8;
use charnames qw( :full :short );
use autodie;
my $row = 5;
my $col = 15;
my $File = "C:/Users/me/Desktop/Reporting-Static/Input-test1.xlsm";
my $parser = Spreadsheet::ParseXLSX->new();
my $workbook = $parser->parse($File);
my $worksheet = $workbook->worksheet("Input");
my $cell = $worksheet->get_cell($row, $col);
my $Filename = $cell->Value();
my $texfile = "C:/Users/me/Desktop/Reporting-Static/file.tex";
# can't find this file if there are spaces in the address
system("perltex", "--latex=pdflatex", "--nosafe", "--jobname=$Filename", "$texfile");
if ( $? == -1 )
{
print "command failed: $!\n";
}
else
{
printf "command exited with value %d", $? >> 8;
}
exit;
However, the moment I change the folder name to one with spaces eg. "Reporting Static" it fails to find the tex file.
I have read several other posts regarding this on stack exchange and other websites but for whatever reason the proposed solutions do not appear to work for me. I have tried
my $texfile = "C:/Users/me/Desktop/Reporting Static/file.tex";
my $texfile = C:/Users/me/Desktop/"Reporting Static"/file.tex;
my $texfile = "\"C:/Users/me/Desktop/"Reporting Static"/file.tex\"";
my $texfile = "\"C:/Users/me/Desktop/Reporting Static/file.tex\"";
my $texfile = "C:/Users/me/Desktop/Reporting^ Static/file.tex";
my $texfile = "C:/Users/me/Desktop/Reporting\^ Static/file.tex";
As well as a few other combinations or varioations of the above, all without success. I have also tried replacing the double quote with a single quote so that perl doesn't interpolate the contents.
I have also tried manually typing all of the above into the command prompt to check whether there was a small issue with the way perl passed the commands to the command line but still no luck.
I am aware that I can use the 'dir /X ~1 c:\' command to find system name allocations that avoid spaces but the idea is that the filename and location will be dynamic and change as a funtion of department and site, so I would prefer to avoid trying to write a script which will go and find this pathname and use it to replace all locations using spaces or other special characters.
The final idea that I had is that this problem could be connected ot the way that perltex passes it's arguments yet I am unable to find any documentation (that I can follow...) on the specifics of how this particular aspect of the file functions.
So my questions are, is there something I am missing not metioned in the other answers that I have read regarding how to correctly pass these paths to perltex, is there perhaps some sort of incompatiblity in how I'm trying to go about this, is this more probabl linked to perltex as opposed to perl or cmd or is there something completely different that I am unaware of that is stopping this from working???
EDIT:
from cmd prompt perltex returns a "unable to find path X, please enter another file location". Until now I hadn't really tested retyping the path by by entering 'C:/Users/me/Desktop/"Reporting Static"/file.tex' (no quotes at the beginning) it is subsequently accepted and runs. but initially passing it this path does not work, suggesting that some internal perltex code accepts the inital path differently to being repassed the same path after an error.... not quite sure what to make of this.
EDIT:
The contents of #latexcmdline that I extracted
$VAR1 = [
'pdflatex',
'--jobname=--',
'\\makeatletter\\def\\plmac#tag{AYNNNUVKQVJGZKKPGPTH}\\def\\plmac#tofile{Perl.topl}\\def\\plmac#fromfile{Perl.frpl}\\def\\plmac#toflag{Perl.tfpl}\\def\\plmac#fromflag{Perl.ffpl}\\def\\plmac#doneflag{Perl.dfpl}\\def\\plmac#pipe{Perl.pipe}\\makeatother\\input C:/Users/me/Desktop/PERLTEST/Perl',
'Modules/RevuedeProjetDB.tex'
];
This was done by inserting
use File::Slurp;
use Data::Dumper;
write_file 'C:\Users\me\Desktop\PERLTEST\mydump.log', Dumper( \#latexcmdline );
before the exec command.
Update
I initially recommended that you should use String::ShellQuote but that module is for Linux only so I deleted my answer when I realised that your question was about the Windows system
It seems that there's also a Win32::ShellQuote which does the same thing for Windows, so I am renewing my suggestion
As I said before, the issue is that perltex itself doesn't properly handle paths containing whitespace, even if they are correctly passed as a single element of #ARGV. I believe the solution is to pass the path including enclosing quotes, although I have never been able to test this properly as I have no LaTex installation
Unfortunately, even if I pass qq{"$texfile"}, the quotes are still stripped before they reach the target program, so they must be protected in some way
You need the quote_system function from that module, which will prepare a list of strings so that they retain any quotation marks
Using a parameter of quote_system(qq{"$texfile"}) produces the correct result in my tests. It is the equivalent of passing qq{"\\"$texfile\\""} but less ugly
So your system call should be like this (with no modification to perltex.pl)
I have applied the same principle to $Filename as it may well be that this also contains whitespace
use Win32::ShellQuote 'quote_system';
system(quote_system(
'perltex',
'--latex=pdflatex',
'--nosafe',
qq{--jobname="$Filename"},
qq{"$texfile"},
));
Okay, well I have a solution of sorts
The issue, as I suspected, is that, although the path is passed as a single string to perltex.pl, the latter doesn't handle paths with spaces properly after it has received them
The temporary fix is to hack perltex.pl
Line 82 of my version of perltex.pl (there is no version number in the source) reads
$latexcmdline[$firstcmd] = "\\input $option";
If you change that to
$latexcmdline[$firstcmd] = qq{\\input "$option"};
then all should be well. However this is a solid fix only when it is distributed by the author of perltex. Meanwhile I am looking for a nicer solution from the calling side
There are two steps to solving this problem.
Work out how to get the correct arguments into an external
program.
Work out how to do that from a Perl program.
For step 1, I find a program like this to be useful.
#!/usr/bin/perl
use strict;
use warnings;
print "Received ", scalar #ARGV, " arguments:\n";
for (1 .. #ARGV) {
print "$_: $ARGV[$_ - 1]\n";
}
It just explains what arguments it receives on the command line. You can use this in place of "perltex" for testing purposes.
You'll see that if you give it an argument that contains spaces, then that is interpreted as the called program as multiple arguments. The way to get round that is to quote the argument that contains a space. And I seem to remember that Windows insists on double-quotes (for reasons that I can never remember).
So I think that you want this:
system('perltex', '--latex=pdflatex', '--nosafe', "--jobname=\"$Filename\"", "\"$texfile\"");
I've double-quoted both of the filenames. Of course, those escaped double-quote characters look really ugly, and Perl gives us qq(...) to make that look nicer.
system('perltex', '--latex=pdflatex', '--nosafe', qq(--jobname="$Filename"), qq("$texfile"));
If that's not quite right, then the program I showed earlier will make it easier to find the solution.
Update: Borodin's comment below about this making no difference to $texfile is accurate. The fact that we're passing a list to system() means that the shell isn't involved at all.

What does 'echo' do within a perl variable declaration?

I am working on transcribing an outdated file from perl to python and got caught up with some perl syntax.
my $jobID = `echo \$JOBID`;
chomp($jobID);
unless ($jobID) {
print "Please specify a job\n";
exit;
}
Thus far, I have been able to transcribe all of the command-line parsing extremely easily but am very fairly stuck here with what exactly this code is doing (specifically the echo within the declaration on line 1).
Within the perl script cmd-line parsing options - that enables one to set the jobID - it states that "default = $JOBID". So my assumption is that the first line in this code simply sets this default jobID until one is specified.
If this is the case why would you need to use echo within the variable default declaration? Is this good practice in perl or am I missing a larger picture?
I have tried searching low and high for this but can't seem to google ninja my way to anything useful.
Any help on the 'echo' would be greatly appreciated (or any good reads on this as well)!
This is one way to get a value from a shell variable. The backticks (`) run the shell command and give you the output. So the echo is running inside of a shell and in this case it just returns the one shell variable. A cleaner way to get this in Perl is to use %ENV like so:
my $jobID = $ENV{'JOBID'};
This also removes the need for chomp, avoids creating an extra process, and is much more efficient.
It is evaluating an environment variable named $JOBID and storing the result in $jobID, which (as duskwuff points out) is better accomplished using $ENV{JOBID}.
The backticks around the echo \$JOBID tell Perl to invoke the specified command in a subshell and return the output of the invoked command.

Perl backtick behaves differently than console commands

So, I'm writing a Perl script which at one point needs to process the output of another script. I tried to do this by calling the script in backticks:
my #output = `scriptName`;
I have tested the script I want to call in backticks, and it works just fine--in the same shell I'm calling my script in, even. But when I call it as part of the script, it produces no output. The variable is left empty.
I've tried executing the command with system(), but there is still no output. I have no idea why. However, the specific arguments I'm passing into the script have caused me problems before until I fixed my PATH variable. Does calling a script through a Perl script result in different environment variables somehow?
Edit: Okay, here's a potential issue. I tried using backticks and system() to print out my PATH variable, and both of them are coming up blank. Is my Perl script unable to use my PATH for some reason?
The most common cause of problems such as these, is difference in relative paths. I have a tendency of using absolute paths for that reason.
In addition, it sounds to me like your subscript might be printing to STDERR and not STDOUT. Backticks only capture STDOUT, so you need to do a redirect with the help of 2>&1
my #output = `scriptName 2>&1`;
NB: The redirect doesn't work with all shells (I believe it was tcsh that didn't support it back when i had a similar problem). Bash takes the redirect just fine.
Environment isn't your problem, unless you're explicitly adjusting it within perl, prior to calling your script. My suggestion would be - double check permissions on your script, and check relative paths. I note you don't have ./scriptName - so if it's not in your path, perl won't be able to find it either.

Storing output of perl script to a file

I"m calling a perl script stored in one PC, for example with name machine1 from say machine2 using the command:
system("perl /CC/builds/123.pl\n");
Now, i need to get the log of the whole perl file executed to be stored in a say 123.txt file and created on machine1.Can any text file be opened in the perl file at the beginning which stored only output of the line executed?
Please help.
Thanks,
Ramki
I'm quite new on that and I don't know if I am understanding your question, but why you don't use "backticks" instead of "system"? It would let you store the output in a variable and then you can do with this variable whatever you want.
About backticks: What's the difference between Perl's backticks, system, and exec?

Calling an application from a perl script

I would like to call an application from a perl script using the 'system' command. However, the application is placed in a separate directory. How can I call the application from that directory in my perl script. Can I use "require"?
You can just change directory if you want to run the application in the directory where the application resides. See the chdir command in perlfunc.
Otherwise, just add the path like this:
system('/path/to/the/application');
No, require doesn't work -- require is used to pull in other perl files, once. If, of course, the file you'd like to call to is a perl program ... well, it's probably better to factor out the parts you'd like to be shared between the two programs, rather than requireing it, or to simply treat it as a black box anyway if you don't want to do that.
To call an executable which is neither in the search path -- simply being in the same directory is not sufficient! -- supply the full path to, e.g. system, exec, &c.. For system, the preferred forms would be:
system { '/path/to/executable' } 'argument 0 (i.e. $0) supplied to program', 'argument 1 (equivalent to $ARGV[0])', ...;
or
# $path = ...
system $path 'arg 0', 'arg 1', ...;
or
# $path = ...
system $path #args;
Of course, it is good form to check the result of each system call in case of errors.
Note the use of indirect-object (i.e. with an argument passed without comma, like the fh for a print or such) system in every case! Unless you know very well what you're doing and why, it's hard to recommend risk using the one-argument form of system, because it subjects your input to the whims of shell preprocessing.
Consult perldoc -fsystem and perldoc -fexec for more detail.