Don't use the shell, then.
#! /usr/bin/perl
use warnings;
use strict;
use Cwd;
use POSIX qw/ strftime /;
my $date = localtime;
my $datef = strftime "%Y%m%d%H%M.%S", localtime;
my $pwd = getcwd;
The result is slightly different: the output of the date
command contains a timezone, but the value of $date
above will not. If this is a problem, follow the excellent suggestion by Chas. Owens below and use strftime
to get the format you want.
Your sub
sub touchandmail {
`touch $cache -t "$datef"`;
`echo "$msg" | mail -s "$subject" $owner -c $sendto`;
}
will fail silently if something goes wrong. Silent failures are nasty. Better would be code along the lines of
sub touchandmail {
system("touch", "-t", $datef, $cache) == 0
or die "$0: touch exited " . ($? >> 8);
open my $fh, "|-", "mail", "-s", $subject, $owner, "-c", $sendto
or die "$0: could not start mail: $!";
print $fh $msg
or warn "$0: print: $!";
unless (close $fh) {
if ($! == 0) {
die "$0: mail exited " . ($? >> 8);
}
else {
die "$0: close: $!";
}
}
}
Using system
rather than backticks is more expressive of your intent because backticks are for capturing output. The system(LIST)
form bypasses the shell and having to worry about quoting arguments.
Getting the effect of the shell pipeline echo ... | mail ...
without the shell means we have to do a bit of the plumbing work ourselves, but the benefit—as with system(LIST)
—is not having to worry about shell quoting. The code above uses many-argument open
:
For three or more arguments if MODE is '|-'
, the filename is interpreted as a command to which output is to be piped, and if MODE is '-|'
, the filename is interpreted as a command that pipes output to us. In the two-argument (and one-argument) form, one should replace dash ('-'
) with the command. See Using open
for IPC in perlipc for more examples of this.
The open
above forks a mail
process, and $fh
is connected to its standard input. The parent process (the code still running touchandmail
) performs the role of echo
with print $fh $msg
. Calling close
flushes the handle's I/O buffers plus a little extra because of how we opened it:
If the filehandle came from a piped open
, close
returns false if one of the other syscalls involved fails or if its program exits with non-zero status. If the only problem was that the program exited non-zero, $!
will be set to 0. Closing a pipe also waits for the process executing on the pipe to exit—in case you wish to look at the output of the pipe afterwards—and implicitly puts the exit status value of that command into $?
and ${^CHILD_ERROR_NATIVE}
.