1

Fairly new to Perl. I have a Perl script on a Linux machine, which has own logfile. Logfile name can change, dependent on data the script is working on (date, filename, datatype, etc.)

The script at some pionts is calling a native executable with system() call, which gives some information out to STDOUT and STDERR - few tens to few hundreds lines over many minutes. After the executable is done, the script continues and logs some other info to the logfile.

Until now the script only logged its own output, without the native executables output, which I want to log in same files as the Perl script logs to. Tried it with following two methods:

#!/usr/bin/perl
#some other code
@array_executable_and_parameters = qw/echo foo/ ;
open $log_fh, '>>', 'log/logfile1.txt';
*STDOUT = $log_fh;
*STDERR = $log_fh;
print "log_fh=$log_fh\n";
system( @array_executable_and_parameters);

$logfilename='log/logfile2.txt';
open(LOGFILEHANDLE, ">>$logfilename" );
*STDOUT = LOGFILEHANDLE;
*STDERR = LOGFILEHANDLE;
print LOGFILEHANDLE "Somethinglogged\n";
system( @array_executable_and_parameters);

It works when I run the script manually, but not when run from cron.

I know it is possible to redirect in the crontab by Linux means, but then I have to know the filename to log to, which only will be known when some data arrives, so seems to me not feasible. I also would like to have as much as possible inside the script, without many dependencies on the Linux etc. I have also no possibility to install any extra modules, libraries for Perl to use, suppose it is bare minimum install.

How do I get STDOUT and STDERR redirected to a specific file from inside the Perl script?

And if possible, how do I detect filename the STDOUT currently goes to?

uldics
  • 117
  • 1
  • 11
  • How is this Perl script invoked, and how exactly are you calling `system()`? – tripleee Dec 21 '17 at 13:07
  • From crontab: * * * * * /home/username/perl/myscript.pl Manually: ./home/username/perl/myscript.pl Executable is called from inside the script like: system( @array_executable_and_parameters); – uldics Dec 21 '17 at 13:11
  • Your interactive example has a dot before `/home` so those are not equivalent unless you are in the root directory; but I assume that's a spurious detail. Can you [edit] your question with a simple `system()` call in the demo script which fails to do what you expect? – tripleee Dec 21 '17 at 13:15
  • For your second question, https://stackoverflow.com/questions/2813092/can-i-find-a-filename-from-a-filehandle-in-perl – tripleee Dec 21 '17 at 13:17
  • Thanks for the edit, We'd still need a good example of what's in the array in order to make this a proper [mcve]. – tripleee Dec 21 '17 at 13:19
  • The contents are pretty long line of relative path to the executable and a pretty long list of arguments, I can not provide at the moment. But they do not matter as the print command acts same, it does not output to the logfile when run from cron, but works as expected when run manually. I hope that helps. – uldics Dec 21 '17 at 13:23
  • As the link in my previous comment explains, you don't need to show us *exactly* the code you are having trouble with. You can, and should, specify a *minimal* code snippet which behaves the way you describe. Can you repro with e.g. `@array_executable_and_parameters = qw/echo foo/` or for stderr `qw/perl -e die/`?? – tripleee Dec 21 '17 at 13:30
  • When adding the echo foo, output from cron runs went to /var/spool/mail/username. – uldics Dec 21 '17 at 13:38
  • I also get it to stdout in interactive use. Are you running this in a directory where the directory `log` does not exist? You are not logging errors from attempting to open the file so we don't know if that is succeeding. – tripleee Dec 21 '17 at 13:46

1 Answers1

4

Reassigning *STDOUT is only affecting the Perl-internal STDOUT scalar's binding. The proper way to redirect standard output on the system level is something like

open (STDOUT, '>&', $log_fh) or die "$0: could not: $!";

You should similarly report errors from your other system calls which could fail (and use strict and etc).

cron runs your job in your home directory, so if the path $HOME/log does not exist, the script will fail to open the log file handle (silently, because you are not logging open errors!)

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • What about `select`? – simbabque Dec 21 '17 at 14:22
  • 1
    Haven't tested that specifically but yeah, TMTOWTDI. – tripleee Dec 21 '17 at 15:00
  • 3
    @simbabque, `select` won't affect `system` either. Only `open (STDOUT, '>&', $log_fh)` and `open (STDOUT, '>', "...")` will change fd 1 – ikegami Dec 21 '17 at 16:17
  • Adding `open (STDOUT, '>>', "$logfilename");` after `open(LOGFILEHANDLE, ">>$logfilename" );` works same way - all good when run manually, but outputs everything to mail file when run from cron. I can not use strict, as the script is failry old and big beast, which needs some improvements many places. I probably am completely unaware of some more missed stuff, thanks for your patience with a Perl beginner here :) – uldics Dec 22 '17 at 06:55
  • So, repeat; are you running it from `cron` in a directory where the `log` directory exists, and are you sure that you were successfully able to open the log file? – tripleee Dec 22 '17 at 06:57
  • Yes, it is run from cron of same user as run manually. Cronline has full path to script file. Same folder that has the script file also has a log folder which already has logfiles in them, from previous manual runs. It makes no difference whether the logfiles exist or not. The problem is that the script does not write to the files when run from cron. Ohh, I get it now! When run manually with full path, then it spits out to STDOUT - my console, not the logfiles! – uldics Dec 22 '17 at 07:35
  • Works as expected only when I am in the script folder, both run with `./scriptname.pl` or full path. When I run it from other folder, with full path and scriptname, then it spits out to STDOUT - console. – uldics Dec 22 '17 at 07:42
  • Exactly, because it cannot create the file in a directory which does not exist. What I'm trying to say is that this is probably what's happening when you run it from `crontab`. Maybe don't hard-code a directory for the log file (though it could still fail if you run it where you don't have write access ... though that's of course less likely). For the record, `cron` jobs run in your home directory. Updated the answer to make this explicit. – tripleee Dec 22 '17 at 07:55
  • That is a bit strange, as my original script has at start `chdir("$ENV{HOME}/perl");`. As well it can write to the relative logfile itself, just the STDOUT from external binary program that refuses to go there. So what is there to do? – uldics Dec 22 '17 at 08:05
  • *For a start, **log your errors*** so you can see what's going wrong. Without access to your environment, we don't know if `$HOME/perl` exists, either. – tripleee Dec 22 '17 at 08:22
  • Yes, it works now, had forgotten in the big script (not test one) `*STDOUT = $log_fh;`. Solution was in multiple details, following few lines were essential: `chdir("$ENV{HOME}/perl"); open(LOGFILEHANDLE, ">>$logfilename" ); open (STDOUT, '>>', "$logfilename"); open (STDERR, '>>', "$logfilename");`. Big thanks for the effort and time spent ;) – uldics Dec 22 '17 at 08:50