record every backtick on STDOUT or some log file


P

perlnewbie

Hi,

I got a Perl script from my colleague which I am going to run on my server.

This script is written to setup some folder structure and to create some users/group/permission/ownership. So, most part of the script uses backtick to call OS commands with some logic behind it. (my question is not about whether backtick is good or bad).

I want to make a minimum changes to the script so that every backtick with variable interpolation (example: `cd $v` should be recorded as 'cd /var/tmp', same as executed by OS) is recorded on STDOUT/STDERR or to some log file..

I don't want to go and add print statement just before every backticks.

What should be the good minimum change (either internal change or thru somewrapper) with or without any modules would help my scenario?

I am looking for a summary of chmod, chown, mkdir OS commands executed by this perl script.

Thanks.
 
Ad

Advertisements

J

J. Gleixner

Hi,

I got a Perl script from my colleague which I am going to run on my server.

This script is written to setup some folder structure and to create some users/group/permission/ownership. So, most part of the script uses backtick to call OS commands with some logic behind it. (my question is not about whether backtick is good or bad).

BTW: Backticks are generally not good, so rewrite it or have your
colleague rewrite it using the correct Perl functions: chdir,
chmod, etc..
I want to make a minimum changes to the script so that every backtick with variable interpolation (example: `cd $v` should be recorded as 'cd /var/tmp', same as executed by OS) is recorded on STDOUT/STDERR or to some log file.

I don't want to go and add print statement just before every backticks.

That's probably the easiest way to do what you want.
What should be the good minimum change (either internal change or thru some wrapper) with or without any modules would help my scenario?

Could also change them into a sub call:

e.g.
`cd $v`;

run( "cd $v" );

sub run
{
my $cmd = shift;

print "Running: $cmd\n";
my $out = `$cmd`;
print "Output: $out\n";
}
 
T

Tim McDaniel

BTW: Backticks are generally not good, so rewrite it or have your
colleague rewrite it using the correct Perl functions: chdir,
chmod, etc..

Opinions may differ. I think that one should use tools that are
well-suited for the work. Personally, if the shell can do it
perfectly well, I'm as inclined to use the shell as not, especially if
it's useful that it affect only itself and not the Perl program.
E.g., maybe I want a bit of script to "cd '$dir' && munge foo.cfg
out.ppp" but I want the Perl program to stay where it is.

But I may be atypical, because I know how to do bullet-resistant
shell coding. It *is* harder to do; much easier to have

mkdir($dir,0777) or crash("my_mkdir: mkdir '$dir' failed: $!");

or use autodie (I think it is).
Could also change them into a sub call:

I recommend that. I usually call it my_system. I usually have it
echo the final command line, and have a global debug boolean that
makes it only do the output without running it, and analyze $? and $!
and such. Maybe I have a log. The advantage of a sub is that you can
do anything.

I see that once I did

use English;

# Borrowed from the perlipc man page.
sub kidopen([email protected]) {
my ($dir, @cmd) = @_;
my $pid;
my $sleep_count = 0;
if ($dir eq '<') {
$dir = '-|';
} elsif ($dir eq '>') {
$dir = '|-';
} else {
die "$0: internal: kidopen() has invalid direction $dir, ";
}
if ($dir eq '|-') {
open(KID, '|less');
return;
}
for (;;) {
$pid = open(KID, $dir);
last if defined $pid;
warn "$0: Cannot fork a child: $!\n";
die "$0: Too many attempts failed. Dying.\n"
if $sleep_count++ > 6;
sleep 10;
}

if ($pid) { # parent
# Return to caller to do something interesting.
} else { # child
exec @cmd or
die("$0: Cannot exec program: $!.\n",
" Program: @cmd");
# NOTREACHED
}
}

sub kidclose() {
$ERRNO = 0;
# print STDERR "=== DEBUG in kidclose\n";
close(KID) or
warn("$0: Child exited with code $CHILD_ERROR, ",
"errno ", 0+$ERRNO, ": $ERRNO.\n");
# print STDERR "=== DEBUG done in kidclose\n";
}

The advantage is that "exec @cmd" means that it will NOT be subject to
the vagarities of shell parsing, which is great if you have one
program with possible unusual arguments that you want not to be
violated by the shell, but really lousy if you want
cd $dir && cp * /scratch/wav
 
R

Rainer Weikusat

Opinions may differ. I think that one should use tools that are
well-suited for the work. Personally, if the shell can do it
perfectly well, I'm as inclined to use the shell as not, especially if
it's useful that it affect only itself and not the Perl program.
E.g., maybe I want a bit of script to "cd '$dir' && munge foo.cfg
out.ppp" but I want the Perl program to stay where it is.

Perl supports fork. Also, when using the Perl functions, it is
possible to implement real error handling because the actual system
error codes are available.
 
T

Tim McDaniel

Perl supports fork.

Quite so, but doing so reliably and well takes some effort, as shown
by "man perlipc".
Also, when using the Perl functions, it is possible to implement real
error handling because the actual system error codes are available.

Except that programs generally do their own error checking on their
own pretty well, like for "mv afile /unwritable", but you have to
implement that in Perl on your own, so that's actually an argument
*against* doing it in Perl.

Except, as I noted, if you're trying to do sophisticated quoting or
control in shell. It takes some knowledge and effort to do
bullet-resistant programming in either shell or Perl, and
unfortunately the skills needed for one often don't translate to the
other.
 
R

Rainer Weikusat

Rainer Weikusat said:
(e-mail address removed) (Tim McDaniel) writes:
[...]
Perl supports fork.

Quite so, but doing so reliably and well takes some effort, as shown
by "man perlipc".

perlipc essentially only mentions fork as part of a daemonization
procedure. But that's not what you get with backticks. These just
instruct perl to fork, exec the shell and pass the command in
backticks as argument to it in a suitable way (which then causes the
shell to fork and exec the actual command, with the shell waiting for
the command and perl waiting for the shell. And any 'result' is only
available as text which needs to be interpreted/ parsed to get at the
actual data). And you can as well just fork the perl process and
execute the system calls changing the process state directly without
affecting the state of the parent process.
Except that programs generally do their own error checking on their
own pretty well, like for "mv afile /unwritable", but you have to
implement that in Perl on your own, so that's actually an argument
*against* doing it in Perl.

Minus output, the only result information perl gets from an external
command is generally "it worked" or "it failed" no matter what that
external command might have done. And this essentially makes it
impossible to implement an _error handling strategies_ in Perl.
 
Ad

Advertisements

T

Tim McDaniel

perlipc essentially only mentions fork as part of a daemonization
procedure.

perlipc in 5.8.8 mentions safe implementations of pipe open (which has
a fork implicitly behind the scenes) and backtick, defining "safe" as
"you can do it without the shell getting its grubby fingerprints on
the arguments".

To be fair, though, my Perl scripts don't usually have a problem with
chdir and such, so that aspect of using shell commands is not often of
use.
Minus output, the only result information perl gets from an external
command is generally "it worked" or "it failed" no matter what that
external command might have done. And this essentially makes it
impossible to implement an _error handling strategies_ in Perl.

In practice, "it worked || it failed" has been good enough for me so
far. The shell programs I've called have been good enough about
reporting their errors for me to realize the underlying problem, and I
can't think of anything different to do depending on which failure
happened.

I'm not saying you're wrong, mind you. If you want to do a mkdir and
need to do different things based on which error happened, or you need
efficiency, then you have to call Perl's mkdir and look at the error
status. It's just that, for me, I haven't needed either, and
therefore things like
system('mkdir', '-p', $tmpdir) or die "cannot create '$tmpdir'.\n";
work for my purposes.

And I certainly don't want to re-implement
system('rm', '-rf', $dir) or die ...
in Perl.
 
R

Rainer Weikusat

perlipc in 5.8.8 mentions safe implementations of pipe open (which has
a fork implicitly behind the scenes) and backtick, defining "safe" as
"you can do it without the shell getting its grubby fingerprints on
the arguments".

That's obviously not relevant in the context of 'executing system
calls which change the state of a process without affecting the state
of "the process which caused that"'. Invoking the safely shell with
input data from untrusted sources "takes some effort" but that's
besides the point.

[...]
And I certainly don't want to re-implement
system('rm', '-rf', $dir) or die ...
in Perl.

That's not particularly difficult and I've probably done so a couple
of times: If I can get by writing a shell scripts, I'll write a shell
script.
 
Ad

Advertisements

C

Chris Davies

perlnewbie said:
example: `cd $v` should be recorded as 'cd /var/tmp', same as executed
by OS

You are aware that `cd $v` (for any value of $v) is a meaningless
operation on a UNIX/Linux type system?

Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top