How to print a string value in single line using Perl - 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.

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.

Coding for external user input in 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.

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.

Can I obtain values from a perl script using a system call from the middle of another perl script?

I'm trying to modify a script that someone else has written and I wanted to keep my script separate from his.
The script I wrote ends with a print line that outputs all relevant data separated by spaces.
Ex: print "$sap $stuff $more_stuff";
I want to use this data in the middle of another perl script and I'm not sure if it's possible using a system call to the script I wrote.
Ex: system("./sap_calc.pl $id"); #obtain printed data from sap_calc.pl here
Can this be done? If not, how should I go about this?
Somewhat related, but not using system():
How can I get one Perl script to see variables in another Perl script?
How can I pass arguments from one Perl script to another?
You're looking for the "backtick operator."
Have a look at perlop, Section "Quote-like operators".
Generally, capturing a program's output goes like this:
my $output = `/bin/cmd ...`;
Mind that the backtick operator captures STDOUT only. So in order to capture everything (STDERR, too) the commands needs to be appended with the usual shell redirection "2>&1".
If you want to use the data printed to stdout from the other script, you'd need to use backticks or qx().
system will only return the return value of the shell command, not the actual output.
Although the proper way to do this would be to import the actual code into your other script, by building a module, or simply by using do.
As a general rule, it is better to use all perl solutions, than relying on system/shell as a way of "simplifying".
myfile.pl:
sub foo {
print "Foo";
}
1;
main.pl:
do 'myfile.pl';
foo();
perldoc perlipc
Backquotes, like in shell, will yield the standard output of the command as a string (or array, depending on context). They can more clearly be written as the quote-like qx operator.
#lines = `./sap_calc.pl $id`;
#lines = qx(./sap_calc.pl $id);
$all = `./sap_calc.pl $id`;
$all = qx(./sap_calc.pl $id);
open can also be used for streaming instead of reading into memory all at once (as qx does). This can also bypass the shell, which avoids all sorts of quoting issues.
open my $fh, '-|', './sap_calc.pl', $id;
while (readline $fh) {
print "read line: $_";
}

Escape whitespace when using backticks

I've had a search around, and from my perspective using backticks is the only way I can solve this problem. I'm trying to call the mdls command from Perl for each file in a directory to find it's last accessed time. The issue I'm having is that in the file names I have from find I have unescaped spaces which bash obviously doesn't like. Is there an easy way to escape all of the white space in my file names before passing them to mdls. Please forgive me if this is an obvious question. I'm quite new to Perl.
my $top_dir = '/Volumes/hydrogen/FLAC';
sub wanted { # Learn about sub routines
if ($File::Find::name) {
my $curr_file_path = $File::Find::name. "\n";
`mdls $curr_file_path`;
print $_;
}
}
find(\&wanted, $top_dir);
If you are JUST wanting "last access time" in terms of of the OS last access time, mdls is the wrong tool. Use perl's stat. If you want last access time in terms of the Mac registered application (ie, a song by Quicktime or iTunes) then mdls is potentially the right tool. (You could also use osascript to query the Mac app directly...)
Backticks are for capturing the text return. Since you are using mdls, I assume capturing and parsing the text is still to come.
So there are several methods:
Use the list form of system and the quoting is not necessary (if you
don't care about the return text);
Use String::ShellQuote to escape the file name before sending to sh;
Build the string and enclose in single quotes prior to sending to sending to the shell. This is harder than it sounds because files names with single quotes defeats your quotes! For example, sam's song.mp4 is a legal file name, but if you surround with single quotes you get 'sam's song.mp4' which is not what you meant...
Use open to open a pipe to the output of the child process like this: open my $fh, '-|', "mdls", "$curr_file" or die "$!";
Example of String::ShellQuote:
use strict; use warnings;
use String::ShellQuote;
use File::Find;
my $top_dir = '/Users/andrew/music/iTunes/iTunes Music/Music';
sub wanted {
if ($File::Find::name) {
my $curr_file = "$File::Find::name";
my $rtr;
return if -d;
my $exec="mdls ".shell_quote($curr_file);
$rtr=`$exec`;
print "$rtr\n\n";
}
}
find(\&wanted, $top_dir);
Example of pipe:
use strict; use warnings;
use String::ShellQuote;
use File::Find;
my $top_dir = '/Users/andrew/music/iTunes/iTunes Music/Music';
sub wanted {
if ($File::Find::name) {
my $curr_file = "$File::Find::name";
my $rtr;
return if -d;
open my $fh, '-|', "mdls", "$curr_file" or die "$!";
{ local $/; $rtr=<$fh>; }
close $fh or die "$!";
print "$rtr\n\n";
}
}
find(\&wanted, $top_dir);
If you're sure the filenames don't contain newlines (either CR or LF), then pretty much all Unix shells accept backslash quoting, and Perl has the quotemeta function to apply it.
my $curr_file_path = quotemeta($File::Find::name);
my $time = `mdls $curr_file_path`;
Unfortunately, that doesn't work for filenames with newlines, because the shell handles a backslash followed by a newline by deleting both characters instead of just the backslash. So to be really safe, use String::ShellQuote:
use String::ShellQuote;
...
my $curr_file_path = shell_quote($File::Find::name);
my $time = `mdls $curr_file_path`;
That should work on filenames containing anything except a NUL character, which you really shouldn't be using in filenames.
Both of these solutions are for Unix-style shells only. If you're on Windows, proper shell quoting is much trickier.
If you just want to find the last access time, is there some weird Mac reason you aren't using stat? When would it be worse than kMDItemLastUsedDate?
my $last_access = ( stat($file) )[8];
It seems kMDItemLastUsedDate isn't always updated to the last access time. If you work with a file through the terminal (e.g. cat, more), kMDItemLastUsedDate doesn't change but the value that comes back from stat is right. touch appears to do the right thing in both cases.
It looks like you need stat for the real answer, but mdls if you're looking for access through applications.
You can bypass the shell by expressing the command as a list, combined with capture() from IPC::System::Simple:
use IPC::System::Simple qw(capture);
my $output = capture('mdls', $curr_file_path);
Quote the variable name inside the backticks:
`mdls "$curr_file_path"`;
`mdls '$curr_file_path'`;