Calling Fortran program from Perl without saving input/output files - perl

I'm using a Perl program to properly format user input into an input file for a Fortran program. The Fortran program creates an output file and error file. The Fortran program is called from Perl like:
system "/mydirectories/fortranexecutable $inputfile $outputfile $errorfile";
I am wondering if there is a way to call the Fortran executable without actually creating the input/output/error files and saving them to the disk before/after the Fortran program is called? I hope my question is clear and not something too obvious. I'm new to Perl and I've tried searching everywhere for this. Thanks for your help in advance.

If the Fortran code reads sequentially from and writes sequentially to already existing files but you would like to communicate with it in "real time" from the Perl code, then you can kind of get around using named pipes. They still exist as entries in the filesystem and can be opened as usual files by the Fortran code given their name but reading/writing from/to them works like piping.
In Perl you would do something like this (blatantly copied from this answer):
use File::Temp qw(tempdir);
use File::Spec::Functions qw(catfile);
use POSIX qw(mkfifo);
my $dir = tempdir(CLEANUP=>1);
my $inputfifo = catfile($dir, "input");
mkfifo($inputfifo, 0700) or die "mkfifo($inputfifo) failed: $!";
my $outputfifo = catfile($dir, "output");
mkfifo($outputfifo, 0700) or die "mkfifo(output$fifo) failed: $!";
my $errorfifo = catfile($dir, "error");
mkfifo($errorfifo, 0700) or die "mkfifo($errorfifo) failed: $!";
... open the FIFOs ...
system "/mydirectories/fortranexecutable $inputfifo $outputfifo $errorfifo";
... operate with the FIFOs to communicate with the Fortran code ...
... close FIFOs and remove $dir when finished ...

No. If Fortran program is written in such way that it takes $inputfile as a command line argument, reads data from it, and outputs $outputfile and $errorfile as a result, the only way to do it is through a file.
If you would prefer to pass input data to Fortran executable through standard input, Fortran source code would have to be modified to accomodate this kind of input.

Related

Unable to Downsample audio file in CGI perl script using sox

I am working on a cgi script where I get an uploaded an audio file, downsample it to 8000Hz and then get it recognised later.
I am facing an error while downsampling the file. The code for downsampling goes like:
1) Code for File Upload:
use CGI;
use strict;
use File::Copy qw(copy);
use CGI::Carp 'fatalsToBrowser';
my $PROGNAME = "file_upload.cgi";
my $cgi = new CGI();
print "Content-type: text/html\n\n";
my $upfile = $cgi->param('upfile');
# Get the basename in case we want to use it.
my $basename = GetBasename($upfile);
no strict 'refs';
if (! open(OUTFILE, ">../cgi-bin/upload/".$basename) ) {
print "Can't open for writing - $!";
exit(-1);
}
2)Code for downsample:
my $source_file="/var/www/cgi-bin/upload/$upfile";
system("sox $source_file -r 8000 /var/www/cgi-bin/upload/temp.wav".";"."mv /var/www/cgi-bin/upload/temp.wav $source_file");
where:
source_file is the path for uploaded audio file
$upfile is the name of the uploaded wav file
temp.wav is the temporary downsampled file which is overwritten on the original file using mv command
Error
sox FAIL formats: can't open input file `/var/www/cgi-bin/upload/file1.wav': WAVE: RIFF header not found
file1.wav is the file I uploaded
Please help me understand why the sox command is not executing despite it being correctly written?
This isn't really an answer to your question as we don't have enough information yet.
Have you tried running the command from your Unix command line? I'd assume you get the same error. What do you get if you run file on the file that you have saved? How big is the file before and after you upload it?
You don't show the code that writes the uploaded file. I suspect there's a bug in that. If you add that to your question, we could help you find it.
Where is GetBasename() defined? Can we see the code?
Your sox command seems strange. You're running sox on a file called temp.wav and then copying that file over your uploaded file. Perhaps there are a couple of steps that you aren't telling us.
Some other suggestions for improvement:
Use cgi->new, not new CGI. The latter has some strange corner cases that you will have real problems debugging if you ever come across them.
If you're loading the CGI module, then why not use its header method instead of writing your own (technically incorrect) header.
no strict 'refs' is a really bad idea (and, as far as I can see, isn't needed here).
Please use the three-arg version of open() and lexical filehandles
open my $out_fh, '>', "../cgi-bin/upload/$basename"
Include the file path in your error message
my $file = "../cgi-bin/upload/$basename";
if (!open my $out_fh, '>', $file) {
print "Can't open file '$file' for writing - $!";
exit(-1);
}
You are loading the File::Copy module, but then moving your file using a shell command.
Allowing random users to upload files into a directory under your cgi-bin directory is a massive potential security hole. You should find another directory to store the uploaded files.
Oh, and then there's the whole - why on Earth would you be writing CGI programs in 2017!
The issue is resolved. The reason why I was having problem executing the sox and copy commands was because of where I was placing the two commands in code. Basically a beginners error. So I was opening the file as mentioned in the problem statement. I put the copy and sox commands for execution before I closed the filehandler and hence they were not getting executed successfully.

How to run set of .exe files in a folder through .bat file using perl script

I am beginner to Perl and I have to create a .pl file and I have folder containing near about 30 exe files(inside Folder1 in G:\Folder1). All of them must be executed by click to the .pl file.
My try is :
use strict; use warnings;
use autodie; # automatic error handling
while (defined(my $file = glob 'C:\shekhar_Axestrack_Intern*.exe'))
{
open my $fh, "<", $file; # lexical file handles, automatic error handling
while (defined( my $line = <$fh> )) {
do system $fh ;
}
close $fh;
}
Please let me know if my logic correct ? Could some one please correct me if i am wrong ?
Use system to execute an exe:
while (my $file = glob 'C:\shekhar_Axestrack_Intern\*.exe') {
system $file;
}
In addition, I have the feeling that you meant to write 'C:\shekhar_Axestrack_Intern*.exe'
instead of 'C:\shekhar_Axestrack_Intern*.exe'.
I think pl2bat may help you. It allows you to wrap Perl code into a batch file.
BTW why are you using echo in your Perl script? You should use print.
Edit: You have edited your question and now you want to know how to run all exe files from a folder using Perl?
Use the system command to run the exe files providing the full path.
See: How to run an executable file using Perl on Windows XP?
Edit 2: do system $fh ; This is not how you do it, please get a book (I'd suggest Beginning Perl by Ovid) and start learning Perl.

Perl script does not create csv file

I have the following code in my perl script which obviously generates a csv file:
open(OUTPUT,">Test.csv");
When I click on the perl script, it generates the output file.
But when I call this script from a command prompt or C# or JAVA, SQL Script (xp_cmdshell), the script does not generate the output file.
I already have full permission for EVERYONE on the folder.
You don't check the success of the operation. Everytime use
open OUTPUT, '>', 'Test.csv' or die $!;
or turn on autodie.

How can I automatically running a large amount of perl scripts?

I need to run over 100 perl scripts (written by the former employee) on Windows for our system stability testing. Each script has several functions, and each function sends certain of linux commands to our back end system, and get results back. The result is written into a log file (currently each script has one log file). The results are “Success”, “Fail”.
Running these perl scripts one-by-one is killing my time. I am thinking about writing a batch file to automate it, but I have to parse the result files to generate test report. I searched online, and seems several testing frameworks, such as Test::Harness, Test::More, Test::Most are good choices. While based on my understanding, they only take .t file, and our scripts are normal perl scripts (.pl), and not standard perl test script (.t script). If using, say, Test::Harness, should I change all the perl script from .pl to .t, and put them under t folder? How to call my functions in Test::Harness? Can someone suggest a better way to automate the testing process and generate the test report like Test::Harness does? I think an example will be very helpful.
Test::Harness and friends isn't really an appropriate choice for this task, unless you want to modify all 100 of your scripts to emit TAP data instead of a log file.
Why not just write a Perl script to run all your Perl scripts?
use strict;
use warnings;
my $script_dir = "/path/to/dir/full/of/scripts";
opendir my $dh, $script_dir or die "Can't open dir $script_dir: $!";
my #scripts = grep { /\.pl$/ } readdir $dh;
foreach my $script( #scripts ) {
print "Running $script\n";
system 'perl', $script;
}
You could even parallelize this using fork and exec (or Parallel::ForkManager, even better), assuming that makes sense for your system.
One of us is confused here. These (100+) perl scripts aren't unit tests right?
If I'm correct keep reading.
Test::* you mentioned aren't really what you're looking for.
Sounds to me like you just need a main.pl, or a .bat, to run each test.pl.
So it seems you're on the right path. If it's possible to have all tests in the same directory, you can do something like this.
my $tests_directory = "/some/path/test_dir";
opendir my $dh, $tests_directory or die"$!";
my #tests = grep { $_ !~ /^\./{1,2}$/ } readdir $dh;
for my $test (#tests) {
system('perl', $test);
}

Perl automated file testing

So I'm taking a Perl programming class and our teacher gave us our first assignment with very little talk about how to actually program perl. Here's exactly what our teacher assigned:
"You should write a script (you may name you script whatever you deem appropriate) that accepts 3 filenames as arguments. The first filename corresponds to the program source code written in C++. The second filename corresponds to an input file that is to be used by the C++ program listed as the first filename. The third filename corresponds to a text file that contains the expected, correct output for the program in question. A directory path may be provided with any of the filenames.
If the program doesn’t require an input file, the second parameter on the command line should be the filename "/dev/null".
All of the files - the program source, the input file, and expected output file - should be copied to a scratch test directory before any of them are used by the script so that there is little chance that the originals will be modified by the test procedure. If the scratch directory doesn’t exist, your script should create one as a subdirectory of the current working directory.
Your script should then compile and link the scratch copy of the source using the GNU g++ compiler. The script should then run the program – saving the output to a temporary file stored in the scratch test directory.
After running the program, your script should then use the UNIX command diff to compare the actual output generated in the previous step with the expected output file and report either that the output conforms to specifications or report any differences as reported by the diff program.
After completion, your script should remove all of the temporary copies and scratch files. Do not remove the original program, the original input file, the original expected output file, or the scratch directory itself."
I have this so far:
#!/usr/bin/perl -w
use strict;
my ($line, $program, $input, $output);
print "Give the program, input, and standart output for testing. ";
$line = <>;
chomp $line;
($program, $input, $output) = split/\s+/, $line; # split/\s+/ is to separate spaces from the input
my($o_test) = $output + "_test";
print "$program ";
print "$input ";
print "$output ";
system("mkdir test_scratch") == 0
or die "failed to create test_scratch. exiting...."
system("cp $program, /test_scratch/"); # error
system("cp $input, /test_scratch/");
system("cp $output, /test_scratch/");
system("cd test_scratch");
system("g++ $program");
system("chmod +x a.out");
system("./a.out < $input > $o_test");
my($DIFF) = system("diff $output $o_test") # error
if[ $DIFF != ""]
print ("Output conforms to specifications."); # error
then
print ("$DIFF");
system("cd ..");
I'm getting errors at the # in code. I don't even know how to do the "/dev/null". I've spent a lot of time looking things up online and searching stackoverflow, but I just don't know what else to do. I realize this is an extremely long question but I don't know what else to do. Thank you for ANY help you can give me.
There are several modules which can help you here. The first one I would recommend is ExtUtils::CBuilder which can manage a build process for you. Then you might also use File::Copy for moving things intot the temporary folder, and even File::chdir for managing the working directory. Since the prof specifies that you should use diff perhaps you should, but there are modules which do that task, or you could use Test::More to check that the output is what is expected.
Just for future reference, this is how I would accomplish a similar task (don't keep temp dir, don't need diff):
#!/usr/bin/env perl
use strict;
use warnings;
use File::chdir;
use File::Temp;
use File::Copy;
use File::Basename;
use File::Slurp;
use ExtUtils::CBuilder;
use Test::More tests => 1;
die "Not enough inputs\n" unless #ARGV >= 3;
# create a temporary directory
my $temp = File::Temp->newdir;
# copy all arguments to that temporary directory
copy $_, $temp for #ARGV;
# store only the filename (not path) of each argument
my ($cpp_file, $in_file, $expected_file) = map { scalar basename $_ } #ARGV;
# change working directory to temporary one (via File::chdir)
local $CWD = $temp;
# build the executable
my $builder = ExtUtils::CBuilder->new(config => {cc => 'g++'});
my $obj = $builder->compile( source => $cpp_file, 'C++' => 1 );
my $exe = $builder->link_executable( objects => 'hello.o' );
# run the executable
my $output = `./$exe $in_file`;
# read in the expected file
my $expected = read_file $expected_file;
# test the resulting output
is $output, $expected, 'Got expected output from executable';
Note that you may need to be careful with newlines on the output and expected files.
I'm getting errors at the # in code.
The easy fix for this error is to remove the comma. The standard Linux cp command doesn't take a comma between the source and destination. You might also consider File::Copy to copy files in Perl.
I don't even know how to do the "/dev/null".
/dev/null is a standard Linux file(*), which points to emptiness. You can write bytes to this file, and they disappear. You can try to read bytes from this file, and nothing's ever there.
This leaves you with two options:
Just use /dev/null directly, and the program will be unable to read anything from an empty byte stream.
Check if the input file is equal to /dev/null, and don't give the C++ program an input value if the input is /dev/null.
Either method would work. If you copy /dev/null to a directory, you get a new zero-length file called 'null', which is a perfectly valid empty input file for the C++ program.
(*) No, /dev/null is not a file in the sense you're used to if you come from the Windows world. It doesn't exist on disk - never has and never will. But the classic Unix philosophy is to make every data source a file, even if that file doesn't exist on disk. As an example, see the Linux /proc filesystem, which allows you to see information about CPUs, processes, etc. in a directory tree structure. That philosophy held up somewhat until the sockets API took a completely different route. If you're looking for an operating system that does make basically everything into a file, including network connections and the screen, look up Plan 9. I would not recommend doing your homework assignments on Plan 9 and expecting them to work on Linux, though.
N.B. Don't forget to check return codes from system, since if g++ fails to compile the C++ program there will be no way to run a compiled program that doesn't exist.