perl system command redirection to log files - perl

I am trying to redirect perl system command to output file with the following code along with time but its not working ??
$cmd="echo hi";
($second, $minute, $hour) = localtime();
$time="$hour:$minute:$second";
system("$time>new.txt");
system("$cmd 1>>new.txt 2>>&1");

If you want to write the variable $time to a text file, open a writeable filehandle and print it to your file instead.
open(my $outfile, '>', 'new.txt');
print $outfile $time;
...
Secondly, your output redirection should read:
1>>new.txt 2>&1
Which means "append STDOUT (1) to new.txt, redirect STDERR (2) to STDOUT (1)". Having >> makes no sense for the second part.
Finally, I (and every other perl programmer) would strongly recommend using strict and warnings pragmas in your scripts. This will help you pick up on any errors or potential problems in your scripting. Once you've done this, all variables must be declared with my, which is a good habit to get in to anyway. So after all that, your script should look something like this:
# recommended pragmas:
use strict;
use warnings;
# declare all new variables with "my"
my $cmd="echo hi";
my ($second, $minute, $hour) = localtime();
my $time="$hour:$minute:$second";
# open a writeable filehandle and print to the filehandle
open(my $outfile, '>', 'new.txt');
print $outfile $time,"\n"; # I've added a newline character here so that
# the time and the command's output are on different lines ;)
system("$cmd 1>>new.txt 2>&1");

Related

Are there any gotchas with open(my $f, '<:encoding(UTF-8)', $n)

I am having a problem that I am unable to reproduce in a manner suitable for Stackoverflow although it's reproducable in my production environment.
The problem occors in a Perl script that, among others, iterates over a file that looks like so:
abc-4-9|free text, possibly containing non-ascii characters|
cde-3-8|hällo wörld|
# comment
xyz-9-1|and so on|
qrs-2-8|and so forth|
I can verify the correctness of the file with this Perl script:
use warnings;
use strict;
open (my $f, '<:encoding(UTF-8)', 'c:\path\to\file') or die "$!";
while (my $s = <$f>) {
chomp($s);
next unless $s;
next if $s =~ m/^#/;
$s =~ m!(\w+)-(\d+)-(\d+)\|([^|]*)\|! or die "\n>$s<\n didn't match on line $.";
}
print "Ok\n";
close $f;
When I run this script, it won't die on line 10 and consequently print Ok.
Now, I use essentially the same construct in a huge Perl script (hence irreproducable for Stackoverflow) and it will die on line 2199 of the input file.
If I change the first line (which is completely unrelated to line 2199) from something like
www-1-1|A line with some words|
to
www-1-1|x|
the script will process line 2199 (but fail later).
Interestingly, this behaviour was introduced when I changed
open (my $f, '<', 'c:\path\to\file') or die "$!";
to
open (my $f, '<:encoding(UTF-8)', 'c:\path\to\file') or die "$!";
Without the :encoding(UTF-8) directive, the script does not fail. Of course, I need the encoding directive since the file contains non-ascii characters.
BTW, the same script runs without problems on Linux.
On Windows, where it fails, I use Strawberry Perl 5.24
I do not have a full and correct explanation of why this is necessary, but you can try opening the file with
'<:unix:encoding(UTF-8)'
This may be related to my question "Why is CRLF set for the unix layer on Windows?" which I noticed when I was trying to figure out stuff which I ended up never figuring out.

Perl webscript doubtful security

Recently with a project I inherited a simple perl script that generates a PDF of the page:
#!/usr/bin/perl
use CGI;
my $file="showdata.pdf";
my $filepath= "/tmp/$file";
system("wkhtmltopdf \"sample.com/showdata.php?".$ENV{"QUERY_STRING"}."\" $filepath");
print ("Content-Type:application/x-download\n");
print ("Content-Disposition: attachment; filename=$file\n\n");
open FILE, "< $filepath" or die "can't open : $!";
binmode FILE;
local $/ = \10240;
while (<FILE>){
print $_;
}
close FILE;
unlink ($filepath);
I am concerned direct substitution variable $ENV{"QUERY_STRING"}. However, in a cursory testing, I did not reveal any problems. I was not able to create/delete files in a known writable directory. I tried not well or problems in the script should not be?
Yes, that's insecure. What if QUERY_STRING was "; rm -fr /;?
Then your system call would be:
wkhtmltopdf "sample.com/showdata.php?"; rm -fr /; /tmp/showdata.pdf
Accessing $ENV{"QUERY_STRING"} directly is insecure. In my case, my digital parameter 'o' must be forced to be integer. There is secure script version:
#!/usr/bin/perl
use CGI;
my $query = new CGI;
my $o = int($query->param('o'));
my $file="showdata.pdf";
my $filepath= "/tmp/$file";
system("wkhtmltopdf \"sample.com/showdata.php?o=".$o."\" $filepath");
print ("Content-Type:application/x-download\n");
print ("Content-Disposition: attachment; filename=$file\n\n");
open FILE, "< $filepath" or die "can't open : $!";
binmode FILE;
local $/ = \10240;
while (<FILE>){
print $_;
}
close FILE;
unlink ($filepath);
If you concern about security, run your Perl script with taint option -T.
For example, the following script will halt your script with warning: Insecure $ENV{PATH} while running with -T switch at ./foo.pl line 4.
#!/usr/bin/perl -T
my $foo = $ENV{FOO};
system("ls -l $foo");
Note: option -t can be used instead of -T if you just need warning.

From inside a perl script can you know the name of the file you are redirecting output to?

So I have:
test.pl > test.log
is there a way to know inside test.pl that I am outputing to 'test.log'? At the end of my script I want to do some manipulation of test.log without hardcoding the name.
Maybe. The following works on Linux, but will not be very portable to other systems...
#!/usr/bin/env perl
use strict;
use warnings;
my $out = readlink("/proc/$$/fd/1");
print STDERR "I am being output to $out\n";
Naturally, this is probably a bad idea. Better to explicitly open the file and write to it in Perl, rather than having the shell set up redirections.
You can redirect standard output from perl, with minimal changes to your script,
test.pl test.log
my ($file) = #ARGV;
if (#ARGV) {
open STDOUT, ">", $file or die $!;
}
print "output is redirected to $file\n";
# use $file at the end

unable to generate error and redirect it to a file in perl

I am trying to redirect my STDOUT and STDERR to some file. I am successful with that to some extent. But i am not able to understand one thing in the below code.
#!/usr/bin/perl
open (STDOUT,">/var/tmp/outfile") or die "problem : $!";
open (STDERR,">>/var/tmp/outfile") or die "problem : $!";
print "$_\n" foreach (1..10);
sdsdf; # buggy line inserted wantedly
I have inserted the last line assuming that perl would throwout an error and that would be redirected to the file but its not happening . My program does not throughout any error onto the screen nor to the outfile. Please help me understand this behavior.
The sdsdf is not generating any errors (if you use strict then you'll see some compile time errors), that's why you are not seeing any messages. Try this:
use warnings;
use strict;
open (STDOUT,">outfile1") or die "problem : $!";
open STDERR, ">&STDOUT";
print "$_\n" foreach (1..10);
die("aaaa"); # buggy line inserted wantedly
Also in your code you are opening the same file twice, this might cause some problems. In the above we first redirect the stdout to a file then redirect stderr to stdout.
Without use strict;,
sdsdf;
is the same as
"sdsdf";
That's one of the reasons you always want to use use strict; use warnings;. Let's start by adding that.
So you want to log all output including compile-time errors to a file. Well, that's not going to happen by redirecting STDERR after your code has been compiled. The best way to do this is from outside your program.
script.pl >/var/tmp/outfile 2>&1
but it can be done from within your program.
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
open(STDOUT, '>', '/var/tmp/outfile')
or die("Can't redirect STDOUT: $!\n");
open(STDERR, '>&', \*STDOUT)
or die("Can't redirect STDERR: $!\n");
}
print "$_\n" foreach (1..10);
sdsdf; # Syntax error

filehandle - won't write to a file

I cannot get the script below to write to the file, data.txt, using a FILEHANDLE. Both the files are in the same folder, so that's not the issue. Since I started with Perl, I have noticed to run scripts, I have to use a full path: c:\programs\scriptname.pl and also the same method to input files. I thought that could be the issue and tried this syntax below but that didn't work either...
open(WRITE, ">c:\programs\data.txt") || die "Unable to open file data.txt: $!";
Here is my script. I have checked the syntax until it makes me crazy and cannot see an issue. Any help would be greatly appreciated!. I'm also puzzled, why the die function hasn't kicked in.
#!c:\strawberry\perl\bin\perl.exe
#strict
#diagnostics
#warnings
#obtain info in variables to be written to data.txt
print("What is your name?");
$name = <STDIN>;
print("How old are you?");
$age = <STDIN>;
print("What is your email address?");
$email = <STDIN>;
#data.txt is in the same file as this file.
open(WRITE, ">data.txt") || die "Unable to open file data.txt: $!";
#print information to data.txt
print WRITE "Hi, $name, you are \s $age and your email is \s $email";
#close the connection
close(WRITE);
How I solved this problem solved.
I have Strawberry Perl perl.exe installed on the c: drive, installed through and using the installer with a folder also on c with my scripts in, which meant I couldn't red/write to a file (directional or using functions, ie the open one) and I always had to use full paths to launch a script. I solved this problem after a suggestion of leaving the interpreter installed where it was and moving my scripts file to the desktop (leave the OS command in the first line of the script where it is as the interpreter is still in the same place it was initially). Now I can run the scripts with one click and read/write and append to file with CMD prompt and using Perl functions with ease.
Backslashes have a special meaning in double-quoted strings. Try escaping the backslashes.
open(WRITE, ">c:\\programs\\data.txt") || die ...;
Or, as you're not interpolating variables, switch to single quotes.
open(WRITE, '>c:\programs\data.txt') || die ...;
It's also worth using the three-argument version of open and lexical filehandles.
open(my $write_fh, '>', 'c:\programs\data.txt') || die ...;
you must use "/" to ensure portability, so: open(WRITE, ">c:/programs/data.txt")
Note: I assume that c:/programs folder exists
You may want to try FindBin.
use strict;
use warnings;
use autodie; # open will now die on failure
use FindBin;
use File::Spec::Functions 'catfile';
my $filename = catfile $FindBin::Bin, 'data.txt';
#obtain info in variables to be written to data.txt
print("What is your name?"); my $name = <STDIN>;
print("How old are you?"); my $age = <STDIN>;
print("What is your email address?"); my $email = <STDIN>;
{
open( my $fh, '>', $filename );
print {$fh} "Hi, $name, you are $age, and your email is $email\n";
close $fh;
}
If you have an access problem when you try to print to data.txt you can change that line to:
print WRITE "Hi, $name, you are \s $age and your email is \s $email" || die $!;
to get more information. A read only file will cause this error message:
Unable to open file data.txt: Permission denied at perl.pl line 12, <STDIN> line 3.