Using commands inside scripts for Perl? - perl

I trying to run a perl command inside a script and here is what I got:
filenumber1.txt:
94088076164765675
The command window looks okay:
C:\Users\Guest\Documents\Prime Numbers>perl -Mntheory=:all -nE "chomp; say next_prime($_);" filenumber1.txt
94088076164765687
I am trying to do this in a script as well called primenumbers.pl and generate another file called log.txt as the output file and filenumber1.txt as the input file:
#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';
use ntheory ":all";
# open filehandle log.txt
open (my $LOG, '>>', 'log.txt');
# select new filehandle
select $LOG;
perl -Mntheory=:all -nE "chomp; say next_prime($_);" filenumber1.txt
primenumbers.pl is in the same directory as filenumbers.txt and something is wrong when I run the script and I do not know what:
C:\Users\Guest\Documents\Prime Numbers>perl primenumbers.pl
Bareword found where operator expected at primenumbers.pl line 7, near ""say next_prime($_);" filenumber1.txt"
(Missing operator before filenumber1.txt?)
syntax error at primenumbers.pl line 7, near ""say next_prime($_);" filenumber1.txt"
Execution of primenumbers.pl aborted due to compilation errors.
I tried fixing it myself, but I only made matters worse, and there were more problems to fix. Does anyone know how to help me from here please on generating the output file "log.txt" without viewing the input file "filenumber1.txt"? Thanks in advance.

The -n switch to perl just wraps the code in while (<>) { ... }, so, to get a similar effect in your own program, just add the loop yourself (untested code; may contain errors):
#!/usr/bin/env perl
use warnings;
use strict;
use feature 'say';
use ntheory ":all";
# open filehandle log.txt
open (my $LOG, '>>', 'log.txt');
# select new filehandle
select $LOG;
while (<>) {
chomp; say next_prime($_);
}
Run this with
primenumbers.pl filenumber1.txt
and you should be set.

Related

How to make output file of one script, as an input file for the another script?

I am currently working on a project where in I am using Perl language to create command line application of one online tool.
There are total nine modules (for each module there is separate Perl script).
This Command Line Application should work in the following way-
Out of these nine modules user would be able to select any number of modules. (in short pipeline should be built).
after running first selected module, output files are generated.
output file of first module should be taken as an input file by the next module selected by the user.
My doubt is how we can make output file of first module as an input file for the next selected module.
It will be a great help if you solve my doubt as I am new to Perl programming.
Thanking you!
Tamar is right. You can use pipe command: "|". You can do this no matter if you're using windows or a unix based operating system.
Here's a simple example of what you're doing:
Code to output data
out.pl
#!/usr/bin/perl
use strict;
use warnings;
my $file = "output.txt";
my $data = "gasp";
unless(-e $file){
open(my $fh, '>', $file);
print $fh $data;
close $fh;
}
Code that takes input file
in.pl
#!/usr/bin/perl
use strict;
use warnings;
my $gaspage = <STDIN>;
chomp $gaspage;
print $gaspage."\n";
Then you just run it with the commands below that can be run within your perl application or just in the terminal:
perl out.pl
cat output.pl | in.pl

perl getopt::Long path to file

I'm trying to open an input file as an argument using the Getopt::Long module
This is the beginning of my script
#! /usr/bin/perl -s
use strict;
use warnings;
use Data::Dumper;
local $Data::Dumper::Useqq = 1;
use Getopt::Long qw(GetOptions);;
my $input='';
GetOptions('input|in=s' => \$input);
open(my $table1,'<', $input) or die "$! - [$input]"; #input file
And this is how I launch the script
$ script.pl -in /path/to/file.txt
I get as output:
No such file or directory - [] at script.pl line 13.
Line 13 is the line with open(....
Is there a mistake in the script?
You are using Perl's built-in option parsing with the -s in your shebang line. In a command like script.pl -in /path/to/file.txt that makes perl set the variable $in to 1 and remove the corresponding entry from #ARGV before Getopt::Long ever sees it
Just remove -s from the shebang line and everything it will work for you
No, there is no mistake in the script. Your code is doing what you told it to do.
It's calling the die "$! - [$input]"; part of that line, because the open returned a false value.
No such file or directory is the content of $!. That's the error it encountered. And between the [] there is the value of $input, which is empty. So there's your problem. You're passing an empty string to open, and that fails.
You are calling it the wrong way.
Getopt::Long requires options that are longer than one letter to be prefixed by --. This means that -in should be --in.
$ script.pl --in /path/to/file.txt
Because you didn't do that, Getopt::Long didn't see your option, and didn't parse it. A bit higher you initialized $input = '', so it stayed the empty string.
You could add a check to make sure that the input file is always provided.
use strict;
use warnings;
use Getopt::Long qw(GetOptions);;
my $input = '';
GetOptions('input|in=s' => \$input);
die 'the --input option is required!' unless $input;
open(my $table1,'<', $input) or die "$! - [$input]"; #input file

How to read a .conf file in Perl

I just created a text test.conf file with some information. How can I read it on Perl?
I am new to Perl and I am not sue would will I need to do.
I tried the following:
C:\Perl\Perl_Project>perl
#!/usr/local/bin/perl
open (MYFILE, 'test.conf');
while (<MYFILE>)
{ chomp; print "$_\n"; }
close (MYFILE);
I tried installing Perl on my laptop that has Windows 7 OS, and using command line.
Instead of using command line, write your program in a file (you can use any editor to write your program, I would suggest use Notepad++) and save as myprogram.pl in the same directory where you have your .conf file.
use warnings;
use strict;
open my $fh, "<", "test.conf" or die $!;
while (<$fh>)
{
chomp;
print "$_\n";
}
close $fh;
Now open a command prompt and go to the same path where you have your both file myprogram.pl and test.conf file and execute your program by typing this:
perl myprogram.pl
You can give full path of your input file inside program and can run your program from any path from command prompt by giving full path of your program:
perl path\to\myprogram.pl
Side note: Always use use warnings; and use strict; at the top of your program and to open file always use lexical filehandle with three arguments with error handling.
This is an extended comment more than an answer, as I believe #serenesat has given you everything you need to execute your program.
When you do "command line" Perl, it's typically stuff that is relatively brief or trivial, such as:
perl -e "print 2 ** 16"
Anything that goes beyond a few lines, and you're probably better off putting that in a file and having Perl run the file. You certainly can put larger programs on the command line, but when it comes to going back in and editing lines, it becomes more of a hassle than a shortcut.
Also, for what it's worth the -n and -p parameters allow you to process the contents of a stream, meaning you could do something like this:
perl -ne "print if /oracle/i" test.conf

Perl 'system' failure messages

Say I have this perl "program" called simple.pl:
#!/usr/bin/perl
use xyz; # xyz is bogus and doesn't exist
And I also have this "program", called simple2.pl:
#!/usr/bin/perl
system("simple.pl");
my $abc = `simple.pl`;
printf("abc %s\n", $abc);
for both system and backtick, I get this message:
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 7.
Can't exec "simple.pl": No such file or directory at scripts/perl/simple2.pl line 9.
Not very useful for the user calling simple2.pl. Is there a way to get a more useful message?
Note. simple.pl does exist in the current directory. The real problem is that simple.pl doesn't compile. simple2 responds by saying simple doesn't exist. it's a misleading message.
If I had a way to even capture the compile message that would be a start.
This means system couldn't find an executable named "simple.pl" on your PATH. If your simple.pl is in the current directory, you could try to change "simple.pl" to "./simple.pl".
Actually, I don't see how to make this message more descriptive. If you were perl, how would you report this error?
BTW, I wouldn't try to run "simple2.pl" from inside of simple2.pl :)
Yes, check to see if the file exists and is executable, and if it isn't, print a more descriptive message.
unless (-ex $filename) {
print "I am unable to execute file $filename.";
}
If perl say it can't find the file, then it can't find the file. And the problem is more your code. Look at this example.
sidburn#sid:~/perl$ cat test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use xyz;
sidburn#sid:~/perl$ cat test2.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('test.pl');
sidburn#sid:~/perl$ cat test3.pl
#!/usr/bin/env perl
use strict;
use warnings;
system('./test.pl');
If you execute test2.pl you get:
sidburn#sid:~/perl$ ./test2.pl
Can't exec "test.pl": No such file or directory at ./test2.pl line 4.
If you execute test3.pl you get:
sidburn#sid:~/perl$ ./test3.pl
Can't locate xyz.pm in #INC (#INC contains: /home/sidburn/perl510/lib/5.10.1/i686-linux /home/sidburn/perl510/lib/5.10.1 /home/sidburn/perl510/lib/site_perl/5.10.1/i686-linux /home/sidburn/perl510/lib/site_perl/5.10.1 .) at ./test.pl line 4.
BEGIN failed--compilation aborted at ./test.pl line 4.
If you don't provide a relative or absolute path then perl lookup the command in your $PATH environment variable. If it is not there it can't find the file.
You need to provide "./" if it is in the current directory. But note "current directory" doesn't mean the directory where your script relies.
If you want the later then you probably want to do a
use FindBin;
with this you can do something like this:
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin;
use File::Spec::Functions;
my $exe = catfile($FindBin::RealBin, 'test.pl');
print $exe, "\n";
system($exe);
if you want to check if system returns correctly, you need to check the return value from the system() command or $? later that holds the value.
if ( $? != 0 ) {
die "Cannot execute $exe.\n";
}
if you want to suppress messages from your program you need to redirect STDOUT, STDERR before starting your program with system().
Or use something like IPC::System::Simple
Or IPC::Open3 (in the core).
Bonus points for enabling the warnings pragma! Have an upvote!
You want to use backticks or qx// to capture the output of an external program, not system. To substitute your own error message that will make sense to your users (more points for you!), then you might do something as in
#! /usr/bin/perl
use strict;
use warnings;
no warnings 'exec';
chomp(my $abc = `simple2.pl`);
if ($? == 0) {
printf("abc %s\n", $abc);
}
else {
die "$0: unable to calculate abc\n";
}
In case you're unfamiliar, $? is
$CHILD_ERROR
$?
The status returned by the last pipe close, backtick command, successful call to wait or waitpid, or from the system operator.
When $? is zero, it indicates success.
Remember that the warnings pragma is lexical, so rather than disabling the warning for the whole program, you might do it for just one sub:
sub calculate_abc {
no warnings 'exec';
# ...
}
If you are trying to execute something you know is a Perl script, why not invoke the interpreter directly rather than dealing with the system knowing how to execute the file?
my $file = 'simple.pl';
-e $file or die "file '$file' not found";
system "perl $file";
# or
print `perl $file`;
to run with the same installation of perl that is running your current script:
system "$^X $file"; # or `$^X $file`
$^X is a special Perl variable that contains the file name of the running interpreter.
I had the exact same issue and figured out that perl wasn't installed. So the bash script was trying to execute the perl without an interpreter.
ls /usr/bin/perl
Try specifying the full path to the "simple.pl" file.

How can I append the file foo2.txt to foo1.txt?

Is there any method to execute foo2.pl from foo1.pl in Perl, and append the foo2.txt to the foo1.txt then create foo3.txt? thanks.
i.e.
foo1.pl
print "Hello"; # output to foo1.txt;
foo2.pl
print "World"; # output to foo2.txt;
How to create foo3.txt file based on foo1.pl.
foo3.txt
Hello
World
Something like append foo2.txt to foo1.txt.
As i know, I can open foo1.txt and foo2.txt, then include the lines in foo3.pl.
print FOO3_TXT (<FOO1_TXT>);
print FOO3_TXT (<FOO2_TXT>);
Is there any good method?
Update my test (ActivePerl 5.10.1)
My foo.pl
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print "world\n";
my hw.pl (foo.pl and hw.pl at the same directory)
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print 'hello ';
print `./foo.pl`;
Output
**D:\learning\perl>hw.pl
hello '.' is not recognized as an internal or external command,
operable program or batch file.**
If hw.pl updated {}:
#!C:\Perl\bin\perl.exe
use strict;
use warnings;
print q{hello }, qx{./foo.pl};
Now Output. (a little different for the loacation of hello)
D:\learning\perl>hw.pl
'.' is not recognized as an internal or external command,
operable program or batch file.
hello
[Update].
Fixed. see answer,
run this as a script
perl foo1.pl > foo3.txt;
perl foo2.pl >> foo3.txt;
contents of foo1.pl
!#/bin/perl
print "Hello";
contents of foo2.pl
!#/bin/perl
print "World";
or
simply use the cat command if you are running linux to append foo2.txt to foo1.txt.
Just in case you are being literal about execute foo2.pl from foo1.pl in Perl then this is what you can do:
print 'hello ';
print qx(perl foo2.pl);
qx is another way to run system commands like backticks. Thus perl foo2.pl is run with the output being sent back to your calling perl script.
So here the same using backticks. Also it uses a direct call to script (which is better):
print 'hello ';
print `./foo2.pl`;
And if you are expecting lots of output from the script then its best not to load it all into memory like above two examples. Instead use open like so:
print 'hello ';
open my $foo2, '-|', './foo2.pl';
print <$foo2>;
close $foo2;
And you can wrap this up into one print statement for "hello world" example:
print 'hello ', do {
open my $foo2, '-|', './foo2.pl';
<$foo2>;
};
/I3az/
Using a shell script (for example, a .bat file on Windows) to run various Perl scripts and combine their output is one way to solve the problem. However, I usually find that Perl itself provides a more powerful and flexible environment than shell scripts. To use Perl in this way, one place to start is by learning about the system and exec commands.
For example:
# In hello.pl
print "Hello\n";
# In world.pl
print "World\n";
# In hello_world.pl.
use strict;
use warnings;
system 'perl hello.pl > hello_world.txt';
system 'perl world.pl >> hello_world.txt';
You can use the following code also
file1.pl
use strict;
use warnings;
open (FH,">file") or die "$! can't open";
print FH "WELCOME\n";
file2.pl
use strict;
use warnings;
open (FH,">>file") or die "$! can't open";
print FH "WELCOME2\n";
The file content is
WELCOME
WELCOME2
If you know beforhand that the script you want to execute from inside the other script is also Perl, you should use do EXPR (https://perldoc.perl.org/functions/do.html).
This executes the contents of the file EXPR in the context of the running perl process and saves you from starting new cmd.exe and perl.exe instances.
hello.pl:
print "Hello";
do "world.pl";
wordl.pl:
print "World";