Coding for external user input in perl - perl

I am new to perl and coding and not entirely sure where/what to search for so this question may have been asked before.
I finnished writing a program in perl and would like to know what code to use that allows me to enter a variable (name) outside the program without needing to specify it in the coding - I was told there is a way to execute the program outside of putty (I use putty) and that it asks me to enter the variable/s beforehand.
in the coding the variable is specified in the beginning as:
my $name='xxx';
after which the name is used for specifying which files to use etc. I have over 30 different names that I need to run individually so it would be much easier if I can just type it in as part of the program instead of changing the coding each time in putty.
Hope my question is clear - I'm still learning the different terms and syntax.
Thanks!

use strict;
use warnings;
open(IN,$ARGV[0]) or die "Cannot open $ARGV[0]:$!\n";
my #in = <IN>;
close(IN);
my $name='';
foreach my $in(#in){
chomp($in);
$name = $in;
###your code here
}
your fle sample
name1
name2
name3
Run your programs as
perl program.pl filename.txt
Update:(after OP's comment)
my $name = <STDIN>;
this will prompt for a user input.

Related

How to print a string value in single line using Perl

My problem is simple one...
My perl code is like below...
$todaydate = `date +%Y-%m-%d-%H%M%S`;
$output_file = "my_data_$todaydate".".csv";
print "SQL query output file name : $output_file\n";
But the Output file name is printing as like below...
SQL query output file name : my_data_2017-10-03-062227
.csv
If you can observe, the .csv is coming in new line.
I have also tried the below join for string conactantion. but still no luck.
$output_file = join "", "my_data_", $todaydate, ".csv";
due to this issue, while i am passing the output_file name to a sql query, its creating a file my_data_2017-10-03-062227 without .csv extension.
Any suggestion please...
There are several reasons why you might not want to use external programs unnecessarily.
The external program might not be available (or might work differently) on some systems where you want to run your program. You are therefore making your code less portable.
Starting a new shell and invoking an external program takes longer than just using a Perl feature to achieve the same result.
The value returned from an external problem will probably have a newline character at the end - and you might forget to remove it.
Getting a date is a task that people commonly want to use an external program for. And I don't understand why, because Perl has pretty good built-in time and date handling. For example, your code can be written like this:
use Time::Piece;
$todaydate = localtime->strftime('%Y-%m-%d-%H%M%S');
$output_file = "my_data_$todaydate.csv";
print "SQL query output file name : $output_file\n";
Time::Piece has been included with all versions of Perl since 2007. It changes the behaviour of localtime() so it returns an object. And the object has many useful methods - here we use strftime().
If you're stuck with an older version of Perl (pre-5.10) then you can still do this easily without calling an external program.
use POSIX 'strftime';
$todaydate = strftime('%Y-%m-%d-%H%M%S', localtime);
$output_file = "my_data_$todaydate.csv";
print "SQL query output file name : $output_file\n";
use chomp in your $todaydate variable.
my $todaydate = `date +%Y-%m-%d-%H%M%S`;
chomp $todaydate;
my $output_file = "my_data_$todaydate.csv";
Always put use warnings; and use strict; in top of the program.
Its because of new line character at the end of $todaydate. Use below to fix it:
$todaydate =~ s/\n|\r//g;
The above code will remove any occurrence of \n or \r.

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.

Finding the standard out for a perl program

I'm redirecting standard out for a perl program. Example:
perl run_program.pl > /log/run_program.log
Is there a way to know what the standard out is. So in this case I'm looking to have the value of '/log/run_program.log'.
If it's not possible is there another/better way to get the same result?
Thanks in advance!
EDIT: The reason I'm not setting STDOUT in the program is because I'm calling a bunch of .pm that have print lines that I want to go to STDOUT with out having to pass the file to it.
On my system, you can use
readlink("/proc/$$/fd/1")
EDIT: The reason I'm not setting STDOUT in the program is because I'm calling a bunch of .pm that have print lines that I want to go to STDOUT with out having to pass the file to it.
Just to let you know, you might be able to use the select command to redefine the FD for the default output:
use strict;
use warnings;
use autodie;
open my $output_fd, ">", "/log/run_program.log";
my $old_default_fd = select( $output_fd );
print "I'm now going into /log/run_program.log\n";
select ($old_default_fd; # Restore the default when you no longer need it
This may work with most of your Perl modules. Just hope that they're not doing something stupid like:
print STDOUT "Ha, ha. I'm still going to STDOUT.\n".
I hate it when Perl modules print stuff.
<soapbox>
To you Perl Module writers:
Perl modules should not be printing (unless that's their main purpose). You should instead return what you want to print and let the caller decide what to do with the output.
</soapbox>
For the first part of your question, no. There's no way for the perl program to know where STDOUT is directed to.
The redirection happens external to the program, and is "wired up" before the perl process even starts. STDOUT could be pointed to a device, a file, or another process (a pipe).
The whole purpose of redirection from stdout to a file is to adapt a program which typically writes to stdout and redirect it to a file. The OS doesn't give you the name of the file, because it figures your program is too stupid to know what to do with a file name.
So your best bet is to get it as my $file_name = shift; and open it yourself. (A shift in the mainline pulls from #ARGV.)
Give a chance to this ideas:
...
my $log_path = "/log/run_program.log"; # or using $0 in some manner
open $log_handler, "<", $log_path or die;
...
Now you could code a myprint subroutine that will call print $log_handler and use it into the whole program, or better, having a look to OVERRIDING CORE FUNCTIONS you could self redefine print doing like this:
...
use subs 'print';
sub print { #redefine here }
...

How to make use of UUIDGEN of unix in perl script

I am trying to generate a unique number using uuidgen (of unix). The generated unique number should get stored in a variable. When i am doing it in a function , I am facing errors.
Can anyone let me know how to make use of uuidgen script in perl.
#!/usr/bin/perl
sub function_uuidgen
{
my myuuid;
system(`export myuuid=uuidgen`);
print "\n unique id is : $myuuid";
# I need not to export the variable, I just need to unique number generated by UUID GENERATOR in a variable.
}
int main
{
print "\n I am in main function";
&function_uuidgen;
}
I am facing the below error when I am running uuidgen as mentioned below. Can anybody help me out with exporting the JAVA VARIABLE in perl ? How to export the path variable,in case if this error is related to that.
Error :
/bin/java: uuidgen 1: not found
Code :
sub function_uuidgen
{
my $myuuid = qx(uuidgen);
chomp $myuuid;
print "\n unique id is : $myuuid";
# I need to export the variable, as it is giving me error without exporting.
}
int main
{
print "\n I am in main function";
function_uuidgen();
}
You're mixing up C and Perl here.
As far as I know you can't access exportet system variables that way from perl (correct me if I am wrong. I don't have much knowledge of linux system variables).
A way to generate UUID's would be the Data::GUID Module from CPAN
use strict;
use warnings;
use Data::GUID qw( guid_string );
my $guid = guid_string();
print "$guid\n";
Also Perl doesn't have an int main function. Your code starts at the top and runs down to the bottom. Of course this gets a bit different if you create an object orientated module.
If you for some reason can't use Data::GUID, this is a way to use the output of uuidgen (note the backticks):
#!/usr/bin/perl
use strict;
use warnings;
my $uuid=`uuidgen`;
chomp $uuid;
print "$uuid\n";
Example output:
$ ./hep.pl
fe82c4f6-a1f2-4242-ab45-853780931927
$
Also, using & before function calls went out of fashion many years ago :-)
Without knowing anything about uuidgen: You could just
my $perlVar = `uuidgen`;
within perl.
Assuming calling uuidgen in your console returns the number you are looking for.
export is a shell command that adds a variable and value to its environment block. The environment block is private to a process, but (by default) copied to a child process. You appear to be thinking it is some sort of global area - it is not.
So, all you would be doing is adding a value to the shell's environment block, not your own! (That's the shell created by system(), not the one you were running from). Placing the export inside back-ticks is strange, if not wrong.
Easier to use:
my $myuuid = qx(uuidgen);
chomp $myuuid;
Notice I am using qx instead of back-ticks `` because they can be confusing (back-ticks are deprecated in UNIX shells as well).
To run the subroutine, loose the C style int main:
print "\n I am in ", __PACKAGE__, "package\n";
function_uuidgen();
The leading & on a subroutine call has side-effects that you probably don't need.

Convert Perl Script to VBA Script

I have a working Perl script that I would like to convert to VBA to run in an Excel macro so that it can easily be shared to other PC's. I have a shell script (where I pass the parameters) driving the perl script.
I used the Perl script to read each specified column of all rows of data from a fixed width file (below the start is 54,63) and compare that data with another file and print the difference. I'd pass parameters in the Shell script as runpro.pl filea.txt fileb.csv > myoutput.txt
Any assistance would be great! Especially if someone can point me in the right direction since the code is fairly simple. Thanks!
#!/usr/bin/perl
#Perl Script runpro.pl
#***************************************************
use strict;
use warnings;
my ($fa, $fb) = #ARGV;
#ARGV = $fa;
my %codes;
while(<>) {
s/[\r\n]+\z//;
$_ = substr($_, 54, 63);
s/\s+\z//;
next if $_ eq "";
$codes{$_} =1;
}
#ARGV = $fb;
my %descrip;
while(<>) {
s/[\r\n]+\z//;
s/,.*//;
s/"//g;
$descrip {$_} = 1 if s/^1234//;
}
for (sort keys %codes) {$
print "$_\n" unless ($descrip{$_});
}
A couple of points:
1) VBA is very different from Perl - so things that are one-liners in one will be tricky in the other
2) If you haven't used VBA in Excel, I suggest you start by "recording" a macro (first make the "Developer" tab in the ribbon visible, then select "record macro", and start doing things like opening files, importing them (fixed width). After you stop the recording you will see the syntax for doing these things - that should help a lot
3) You will have to decide how you want to pass arguments to VBA - cells on a worksheet, dialog box... There is no such thing as "running VBA from the command line".
I wonder if you really need / want VBA or if you would be better off compiling a standalone program (.exe). Is this meant to run on PC (windows), Mac OS, or both? See for example this earlier question - maybe that's what you actually need (if not what you asked for...)?