I started using Term::Readline recently, but now I realized cat text | ./script.pl doesn't work (no output).
script.pl snippet before (working ok):
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
while (<>) {
print $_;
}
script.pl snippet after (working only interactively):
#!/usr/bin/perl
use strict;
use warnings;
use Term::ReadLine
$| = 1;
my $term = Term::ReadLine->new('name');
my $input;
while (defined ($input = $term->readline('')) ) {
print $input;
}
Is there anything I can do to preserve this behavior (to have the lines printed) ?
You need to set it up to use the input and output filehandles that you want. The docs don't spell it out, but the constructor takes either a string (to serve as a name), or that string and globs for input and output filehandles (need both).
use warnings;
use strict;
use Term::ReadLine;
my $term = Term::ReadLine->new('name', \*STDIN, \*STDOUT);
while (my $line = $term->readline()) {
print $line, "\n";
}
Now
echo "hello\nthere" | script.pl
prints the two lines with hello and there, while scipt.pl < input.txt prints out the lines of the file input.txt. After this the normal STDIN and STDOUT will be used by the module's $term for all future I/O. Note that the module has methods for retrieving input and output filehandles ($term->OUT and $term->IN) so you can change later where your I/O goes.
The Term::ReaLine itself doesn't have much detail but this is a front end for other modules, listed on the page. Their pages have far more information. Also, I believe that uses of this are covered elsewhere, for example in the good old Cookbook.
Related
I need to know the CGI variables in my script. I know I can use
$query->param()
to get CGI parameters, but how can I get module paths and the version of Perl?
you question is really vague..
to get the complete environment and all installed modules use this:
#!"\xampp\perl\bin\perl.exe"
use strict;
use warnings;
use File::Find ;
use CGI qw(:standard);
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
# use sneaky cgi direct printing ;)
$|=1 ;
# define subs
sub permodules;
# print header
print header;
print start_html("Environment");
# get perl version
print `perl -v`;
# get env vars
foreach (sort keys %ENV) {
print $ENV{$_};
}
# get inc vars
print #INC;
foreach my $path (#INC) {
next if ($path =~ /^\./) ;
find(\&perlmodules,$path) ;
}
# get mods
my (#mods,%mods);
#mods=sort {lc($a) cmp lc($b)} keys %mods;
my $amount=int($#mods/3+.9999) ;
for (my $mod=0 ; $mod<$amount ; $mod++) {
print $mods[$mod] . "-" . $mods[$mod+1*$amount] . "-" . $mods[$mod+2*$amount] ;
}
sub perlmodules {
if ($File::Find::name =~ /\.pm$/){
open(MODFILE,$File::Find::name) || return;
while(<MODFILE>){
if (/^ *package +(\S+);/) {
$mods{$1}=1 ;
last;
}
}
close(MODFILE) ;
}
}
# end
print end_html;
adapt the print statements to fit your needs.
Yes, everything is stored in the %ENV hash. Here's a good example of how to display them:
#!/usr/bin/perl -wT
use strict;
use CGI qw(:standard);
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
print header;
print start_html("Environment");
foreach my $key (sort(keys(%ENV))) {
print "$key = $ENV{$key}<br>\n";
}
print end_html;
EDIT: Alex's answer is more comprehensive, and will return module information as well.
Read the docs they are very good.
http://perldoc.perl.org/CGI.html
They cover almost everything you'll run into.
%ENV and such
http://perldoc.perl.org/CGI.html#FETCHING-ENVIRONMENT-VARIABLES
When you "use" a module (i.e., "use File::Find;") Perl loads the first occurrence of that module that it finds in the #INC and sticks it into %INC. For example, if you're looking to find which version of File::Find you're using, you can simply print the corresponding entry from %INC.
The command will look like this:
perl -MFile::Find -e 'print $INC{"File/Find.pm"} . "\n";'
Which prints this on my mac:
/usr/local/ActivePerl-5.16/lib/File/Find.pm
I am very new to Perl. I wrote a script to display user name from Linux passwd file.
It displays list of user name but then it also display user ids (which I am not trying to display at the moment) and at the end it displays "List of users ids and names:" which it should display before displaying list of names.
Any idea why it is behaving like this?
#!/usr/bin/perl
#names=system("cat /etc/passwd | cut -f 1 -d :");
#ids=system("cat /etc/passwd | cut -f 3 -d :");
$length=#ids;
$i=0;
print "List of users ids and names:\n";
while ($i < $length) {
print $names[$i];
$i +=1;
}
Short answer: system doesn't return output from a command; it returns the exit value. As the output of the cut isn't redirected, it prints to the current STDOUT (e.g. your terminal). Use open or qx// quotes (aka backticks) to capture output:
#names = `cat /etc/passwd | cut -f 1 -d :`;
As you are still learning Perl, here is a write-up detailing how I'd solve that problem:
First, always use strict; use warnings; at the beginning of your script. This helps preventing and detecting many problems, which makes it an invaluable help.
Next, starting a shell when everything could be done inside Perl is inefficient (your solution starts six unneccessary processes (two sets of sh, cat, cut)). In fact, cat is useless even in the shell version; just use shell redirection operators: cut ... </etc/passwd.
To open a file in Perl, we'll do
use autodie; # automatic error handling
open my $passwd, "<", "/etc/passwd";
The "<" is the mode (here: reading). The $passwd variable now holds a file handle from which we can read lines like <$passwd>. The lines still contain a newline, so we'll chomp the variable (remove the line ending):
while (<$passwd>) { # <> operator reads into $_ by default
chomp; # defaults to $_
...
}
The split builtin takes a regex that matches separators, a string (defaults to $_ variable), and a optional limit. It returns a list of fields. To split a string with : seperator, we'll do
my #fields = split /:/;
The left hand side doesn't have to be an array, we can also supply a list of variables. This matches the list on the right, and assigns one element to each variable. If we want to skip a field, we name it undef:
my ($user, undef, $id) = split /:/;
Now we just want to print the user. We can use the print command for that:
print "$user\n";
From perl5 v10 on, we can use the say feature. This behaves exactly like print, but auto-appends a newline to the output:
say $user;
And voilĂ , we have our final script:
#!/usr/bin/perl
use strict; use warnings; use autodie; use feature 'say';
open my $passwd, "<", "/etc/passwd";
while (<$passwd>) {
chomp;
my ($user, undef, $id) = split /:/;
say $user;
}
Edit for antique perls
The autodie module was forst distributed as a core module with v10.1. Also, the feature 'say' isn't available before v10.
Therefore, we must use print instead of say and do manual error handling:
#!/usr/bin/perl
use strict; use warnings;
open my $passwd, "<", "/etc/passwd" or die "Can't open /etc/passwd: $!";
while (<$passwd>) {
chomp;
my ($user, undef, $id) = split /:/;
print "$user\n";
}
The open returns a false value when it fails. In that case, the $! variable will hold the reason for the error.
For reading of system databases you should use proper system functions:
use feature qw(say);
while (
my ($name, $passwd, $uid, $gid, $quota,
$comment, $gcos, $dir, $shell, $expire
)
= getpwent
)
{
say "$uid $name";
}
If you're scanning the entire password file, you can use getpwent():
while( my #pw = getpwent() ){
print "#pw\n";
}
See perldoc -f getpwent.
I am reviewing for a test and I can't seem to get this example to code out right.
Problem: Write a perl script, called ileaf, which will linterleave the lines of a file with those of another file writing the result to a third file. If the files are a different length then the excess lines are written at the end.
A sample invocation:
ileaf file1 file2 outfile
This is what I have:
#!/usr/bin/perl -w
open(file1, "$ARGV[0]");
open(file2, "$ARGV[1]");
open(file3, ">$ARGV[2]");
while(($line1 = <file1>)||($line2 = <file2>)){
if($line1){
print $line1;
}
if($line2){
print $line2;
}
}
This sends the information to screen so I can immediately see the result. The final verson should "print file3 $line1;" I am getting all of file1 then all of file2 w/out and interleaving of the lines.
If I understand correctly, this is a function of the use of the "||" in my while loop. The while checks the first comparison and if it's true drops into the loop. Which will only check file1. Once file1 is false then the while checks file2 and again drops into the loop.
What can I do to interleave the lines?
You're not getting what you want from while(($line1 = <file1>)||($line2 = <file2>)){ because as long as ($line1 = <file1>) is true, ($line2 = <file2>) never happens.
Try something like this instead:
open my $file1, "<", $ARGV[0] or die;
open my $file2, "<", $ARGV[1] or die;
open my $file3, ">", $ARGV[2] or die;
while (my $f1 = readline ($file1)) {
print $file3 $f1; #line from file1
if (my $f2 = readline ($file2)) { #if there are any lines left in file2
print $file3 $f2;
}
}
while (my $f2 = readline ($file2)) { #if there are any lines left in file2
print $file3 $f2;
}
close $file1;
close $file2;
close $file3;
You'd think if they're teaching you Perl, they'd use the modern Perl syntax. Please don't take this personally. After all, this is how you were taught. However, you should know the new Perl programming style because it helps eliminates all sorts of programming mistakes, and makes your code easier to understand.
Use the pragmas use strict; and use warnings;. The warnings pragma replaces the need for the -w flag on the command line. It's actually more flexible and better. For example, I can turn off particular warnings when I know they'll be an issue. The use strict; pragma requires me to declare my variables with either a my or our. (NOTE: Don't declare Perl built in variables). 99% of the time, you'll use my. These variables are called lexically scoped, but you can think of them as true local variables. Lexically scoped variables don't have any value outside of their scope. For example, if you declare a variable using my inside a while loop, that variable will disappear once the loop exits.
Use the three parameter syntax for the open statement: In the example below, I use the three parameter syntax. This way, if a file is called >myfile, I'll be able to read from it.
**Use locally defined file handles. Note that I use my $file_1_fh instead of simply FILE_1_HANDLE. The old way, FILE_1_HANDLE is globally scoped, plus it's very difficult to pass the file handle to a function. Using lexically scoped file handles just works better.
Use or and and instead of || and &&: They're easier to understand, and their operator precedence is better. They're more likely not to cause problems.
Always check whether your open statement worked: You need to make sure your open statement actually opened a file. Or use the use autodie; pragma which will kill your program if the open statements fail (which is probably what you want to do anyway.
And, here's your program:
#! /usr/bin/env perl
#
use strict;
use warnings;
use autodie;
open my $file_1, "<", shift;
open my $file_2, "<", shift;
open my $output_fh, ">", shift;
for (;;) {
my $line_1 = <$file_1>;
my $line_2 = <$file_2>;
last if not defined $line_1 and not defined $line_2;
no warnings qw(uninitialized);
print {$output_fh} $line_1 . $line_2;
use warnings;
}
In the above example, I read from both files even if they're empty. If there's nothing to read, then $line_1 or $line_2 is simply undefined. After I do my read, I check whether both $line_1 and $line_2 are undefined. If so, I use last to end my loop.
Because my file handle is a scalar variable, I like putting it in curly braces, so people know it's a file handle and not a variable I want to print out. I don't need it, but it improves clarity.
Notice the no warnings qw(uninitialized);. This turns off the uninitialized warning I'll get. I know that either $line_1 or $line_3 might be uninitialized, so I don't want the warning. I turn it back on right below my print statement because it is a valuable warning.
Here's another way to do that for loop:
while ( 1 ) {
my $line_1 = <$file_1>;
my $line_2 = <$file_2>;
last if not defined $line_1 and not defined $line_2;
print {$output_fh} $line_1 if defined $line_1;
print {$output_fh} $line_2 if defined $line_2;
}
The infinite loop is a while loop instead of a for loop. Some people don't like the C style of for loop and have banned it from their coding practices. Thus, if you have an infinite loop, you use while ( 1 ) {. To me, maybe because I came from a C background, for (;;) { means infinite loop, and while ( 1 ) { takes a few extra milliseconds to digest.
Also, I check whether $line_1 or $line_2 is defined before I print them out. I guess it's better than using no warning and warning, but I need two separate print statements instead of combining them into one.
Here's another option that uses List::MoreUtils's zip to interleave arrays and File::Slurp to read and write files:
use strict;
use warnings;
use List::MoreUtils qw/zip/;
use File::Slurp qw/read_file write_file/;
chomp( my #file1 = read_file shift );
chomp( my #file2 = read_file shift );
write_file shift, join "\n", grep defined $_, zip #file1, #file2;
Just noticed Tim A has a nice solution already posted. This solution is a bit wordier, but might illustrate exactly what is going on a bit more.
The method I went with reads all of the lines from both files into two arrays, then loops through them using a counter.
#!/usr/bin/perl -w
use strict;
open(IN1, "<", $ARGV[0]);
open(IN2, "<", $ARGV[1]);
my #file1_lines;
my #file2_lines;
while (<IN1>) {
push (#file1_lines, $_);
}
close IN1;
while (<IN2>) {
push (#file2_lines, $_);
}
close IN2;
my $file1_items = #file1_lines;
my $file2_items = #file2_lines;
open(OUT, ">", $ARGV[2]);
my $i = 0;
while (($i < $file1_items) || ($i < $file2_items)) {
if (defined($file1_lines[$i])) {
print OUT $file1_lines[$i];
}
if (defined($file2_lines[$i])) {
print OUT $file2_lines[$i];
}
$i++
}
close OUT;
I was wondering how to import a Perl file to a script. I experimented with use, require and do, but nothing seems to work for me. This is how I did it with require:
#!/usr/bin/perl
require {
(equations)
}
print "$x1\n";
Is it possible to code for substituting a value (I get in my script) into equations.pl, then have my script use an equation defined in equations.pl to calculate another value? How do I do this?
You can require a .pl file, which will then execute the code in it, but in order to access variables, you need a package, and either "use" instead of require (the easy way) or via Exporter.
http://perldoc.perl.org/perlmod.html
Simple example: here's the stuff you want to import, name it Example.pm:
package Example;
our $X = 666;
1; # packages need to return true.
And here's how to use it:
#!/usr/bin/perl -w
use strict;
use Example;
print $Example::X;
This presumes Example.pm is in the same directory, or the top level of an #INC directory.
equations.pm file:
package equations;
sub add_numbers {
my #num = #_;
my $total = 0;
$total += $_ for #num;
$total;
}
1;
test.pl file:
#!/usr/bin/perl -w
use strict;
use equations;
print equations::add_numbers(1, 2), "\n";
output:
3
You can't import a file. You can execute a file and import symbols (variables and subs) from it. See Perl Modules in perlmod.
You've given very few details about equations.pl, but if the input can be given via a command line argument, then you can open a pipe:
use strict;
use warnings;
my $variable; #the variable that you will get from equations.pl
my $input=5; #the input into equations.pl
open (my $fh,"-|","perl equations.pl $input") or die $!;
while(my $output=<$fh>)
{
chomp($output); #remove trailing newline
$variable=$output;
}
if(defined($variable))
{
print "It worked! \$variable=$variable\n";
}
else
{
print "Nope, \$variable is still undefined...\n";
}
If this is the body of equations.pl:
use strict;
use warnings;
my $foo=$ARGV[0];
$foo++;
print "$foo\n";
Then the code above outputs:
It worked! $variable=6
I've created this script below for a assignment I have. It asks for a text file, checks the frequency of words, and lists the 10 words that appear the most times. Everything is working fine, but I need this script to be able to start via the command line as well as via the standard input.
So I need to be able to write 'perl wfreq.pl example.txt' and that should start the script and not ask the question for a text file. I'm not sure how to accomplish this really. I think I might need a while loop at the start somewhere that skips the STDIN if you give it the text file on a terminal command line.
How can I do it?
The script
#! /usr/bin/perl
use utf8;
use warnings;
print "Please enter the name of the file: \n" ;
$file = <STDIN>;
chop $file;
open(my $DATA, "<:utf8", $file) or die "Oops!!: $!";
binmode STDOUT, ":utf8";
while(<$DATA>) {
tr/A-Za-z//cs;
s/[;:()".,!?]/ /gio;
foreach $word (split(' ', lc $_)) {
$freq{$word}++;
}
}
foreach $word (sort { $freq{$b} <=> $freq{$a} } keys %freq) {
#fr = (#fr, $freq{$word});
#ord = (#ord, $word);
}
for ($v =0; $v < 10; $v++) {
print " $fr[$v] | $ord[$v]\n";
}
Instead of reading from <STDIN>, you can read from <> to get data either from files provided on the command line or from stdin if there are no files.
For example, with the program:
#!/usr/bin/env perl
while (<>) {
print $_;
}
The command ./script foo.txt will read and print lines from foo.txt, while ./script by itself will read and print lines from standard input.
You need to do the following:
my $DATA;
my $filename = $ARGV[0];
unless ($filename) {
print "Enter filename:\n";
$filename = <STDIN>;
chomp $filename;
}
open($DATA, $filename) or die $!;
Though I have to say, user-prompts are very un-Unix like.
perl script.pl < input.txt
The use of the operator < passes input.txt to script.pl as standard input. You can then skip querying for the filename. Otherwise, use $ARGV[0] or similar, if defined.
You can check for a command-line argument in #ARGV, which is Perl's array that automagically grabs command line arguments, and --if present-- process them (else continue with input from STDIN). Something like:
use utf8;
use strict; #Don't ever forget this! Always, always, ALWAYS use strict!
use warnings;
if(#ARGV)
{
#Assume that the first command line argument is a file to be processed as in your original code.
#You may or may not want to care if additional command line arguments are passed. Up to you.
}
else
{
#Ask for the filename and proceed as normal.
}
Note that you might want to isolate the code for processing the file name (i.e., the opening of DATA, going through the lines, counting the words, etc.) to a subroutine that you can easily call (perhaps with an argument consisting of the file name) in each branch of the code.
Oh, and to make sure I'm clear about this: always use strict and always use warnings. If you don't, you're asking for trouble.