Kyle Kanos
Kyle Kanos

Reputation: 3264

How do I make Perl’s system() format arguments using scientific notation?

From my Perl program, I am trying to run another command written in C using system. The command requires several arguments: a string, a float, two integers, a pair of floats, and another string. I am running this as

my $arg1="electron";
my $arg2=0.511;
# more definitions
system("./fermions $arg1 $arg2 $arg3 $arg4 " .
       "$arg5 $arg6 \"string\" > outfile.out");

I need to vary $arg5 to be several different float values. I made it work by setting $arg5="1.0e5" and then running an if-statement in the for-loop to change the value as a string. I would like to be able to do this as floats, and tried

system("./fermions $arg1 $arg2 $arg3 $arg4 " .
       "%e $arg6 \"string\" >outfile.out",
       $arg5);

but that did not work. Is there another alternative, or is my if-statement option the only one?

Upvotes: 2

Views: 414

Answers (2)

Greg Bacon
Greg Bacon

Reputation: 139691

Important: You should always check the return value from Perl’s system function to determine whether the command failed.

Use Perl’s sprintf to format the float values as in the code below. Yes, you may be able to get away with using the command as your format specifier, but you’re likely to get surprising results if the command has stray % characters elsewhere. Using two steps is safer.

#! /usr/bin/env perl

use strict;
use warnings;

my @float_values = (1.0e5, 3.14159, 2.71828);

for my $f (@float_values) {
  my $arg5 = sprintf "%e", $f;

  system(qq[./fermions $arg5 "string" >> outfile.out]) == 0
    or warn "$0: fermions failed";
}

In case you aren’t familiar with the syntax, qq[...] works like a double-quoted string, but the different delimiter means you don’t have to escape double-quotes in your command.

Note that I elided the other arguments for typographical purposes, but you can interpolate them along with the value of $arg5. Another subtle change is the switch to >> for appending rather than > for clobbering.

Using a stand-in for fermions

#! /usr/bin/env perl
$" = "][";
warn "[@ARGV]\n";

the output of the two programs running together is

[1.000000e+05][string]
[3.141590e+00][string]
[2.718280e+00][string]

With respect to terminology, a system call refers to a low-level request for service from the operating system, e.g., open, close, unlink, and so on. Although Perl’s system function makes use of system calls, the two concepts are distinct.

To be really safe about the shell not fudging your command-line arguments, use the techniques described in the “Safe Pipe Opens” section of perlipc. Perl’s system and exec functions bypass the shell when given a list of arguments rather than a single string containing the entire command.

Your situation is a little trickier because you want to redirect the standard output. The code below forks a child, sets the child’s STDOUT to append to outfile.out, and then runs fermion with exec in the child. The parent waits for the child to exit and reports any failure.

#! /usr/bin/env perl

use strict;
use warnings;

my @float_values = (1.0e5, 3.14159, 2.71828);

for my $f (@float_values) {
  my $arg5 = sprintf "%e", $f;

  my $pid = fork;
  if (defined $pid) {
    if ($pid == 0) {
      my $path = "outfile.out";
      open STDOUT, ">>", $path or die "$0: open $path: $!";
      exec "./fermions", $arg5, "string" or die "$0: exec: $!";
    }
    else {
      local $!;
      my $pid = waitpid $pid, 0;
      warn "$0: waitpid: $!" if $pid == -1 && $!;
      warn "$0: fermion exited " . ($? >> 8) if $?;
    }
  }
  else {
    die "$0: fork: $!";
  }
}

Upvotes: 1

user149341
user149341

Reputation:

If you want to use printf notation (like "%e"), you need to use the Perl sprintf builtin. Otherwise you just end up passing "%e" as a literal argument.

Upvotes: 6

Related Questions