cygwin perl subroutine executing out of order - perl

I wrote this simple program with a subroutine.
It acts strangely when I put it in a sub routine.
#!/usr/bin/perl -w
use strict ;
sub get_name {
print "what is your name ?\n" ;
my $name = <STDIN> ;
chomp($name) ;
print "Hello $name\n";
}
get_name ;
This is the result
$ ./name_print
casper
what is your name ?
Hello casper
It never prints the what is your name string.
It waits for me to enter in some standard input , and then prints the "what is your name string". It executes out of order.
Very strange.

This type of behaviour can be created if perl is confused about what type of buffering it should do to the IO. Try adding the following line.
$|++;
Just after your 'use strict;' to force perl to flush the buffers after every print statement.

Related

Calling one file from another file gives print value instead of return return value in perl

I can execute one Perl file from another Perl file using the command
`perl -X file-path -arguments=value`
but this gives the print value from the file instead of giving the return value.
This is abc.pl:
my $a = `perl -X def.pl -num=4`;
print "here a is $a";
This is def.pl:
print "in def file";
my $xy = "hello world!";
return $xy;
When invoke the file as perl -X abc.pl, I expect the output to be
here a is hello world!
but I am getting
Can't return outside a subroutine at def.pl line 3.
here a is in def file
How can I achieve this?
-X suppresses warnings. Don't suppress them, they give the hint why the code is failing your expectations.
You should be using subroutines and libraries.
def.pl
sub hello {
print "in def file\n";
my $xy = "hello world!";
return $xy;
}
1;
abc.pl
require './def.pl';
my $a = hello();
print "here a is $a";
Next step up from here is organising your code as modules.
When you use the `command` in Perl, the result is the standard output of the called command. This is similar to shell similar syntax.
Given 'def.pl' is perl program, you can execute it directly from abc.pl, using 'require'. However, 'def.pl' must be a valid perl program. Executing 'return $xy' outside a function will result in error message: Can't return outside a subroutine at - line 1.
Consider creating a function in 'def.pl' that returns the value of $xy. The alternative, which is less than ideal, is to use global.
# abc.pl
require 'def.pl' ;
my $a = xy() ;
print "here a is $a";
# def.pl
sub xy {
my $xy = "..." ;
return $xy ;
}
Alternatively, you can make def.pl 'print' the result, making it possible to retrieve the 'result' from the def.pl script.
# def.pl
warn "in def file";
my $xy = "hello world!";
print $xy;

perl error: Use of uninitialized value $_ in concatenation (.) or string

I get the following error:
Use of uninitialized value $_ in concatenation (.) or string at checkfornewfiles.pl line 34.
when attempting to run the following code :
#!/usr/bin/perl -w
#Author: mimo
#Date 3/2015
#Purpose: monitor directory for new files...
AscertainStatus();
######### start of subroutine ########
sub AscertainStatus {
my $DIR= "test2";
####### open handler #############
opendir (HAN1, "$DIR") || die "Problem: $!";
########## assign theoutput of HAN1 to array1 ##########
my #array1= readdir(HAN1);
######## adding some logic #########
if ("$#array1" > 1) { #### if files exists (more than 1) in the directory #######
for (my $i=0; $i<2; $i++) {shift #array1;} ####### for i in position 0 (which is the . position) loop twice and add one (the position ..) get rid of them #######
MailNewFiles(#array1);
} else { print "No New Files\n";}
}
sub MailNewFiles {
$mail= "sendmail";
open ($mail, "| /usr/lib/sendmail -oi -t" ) ||die "errors with sendmail $!"; # open handler and pipe it to sendmail
print $mail <<"EOF"; #print till the end of fiEOF
From: "user";
To: "root";
Subject: "New Files Found";
foreach (#_) {print $mail "new file found:\n $_\n";}
EOF
close($mail);
}
#End
I am new to perl and I don't know what's going wrong. Can anyone help me ?
A few suggestions:
Perl isn't C. Your main program loop shouldn't be a declared subroutine which you then execute. Eliminate the AscertainStatus subroutine.
Always, always use strict; and use warnings;.
Indent correctly. It makes it much easier for people to read your code and help analyze what you did wrong.
Use a more modern Perl coding style. Perl is an old language, and over the years new coding style and techniques have been developed to help you eliminate basic errors and help others read your code.
Don't use system commands when there are Perl modules that can do this for you in a more standard way, and probably do better error checking. Perl comes with the Net::SMTP that handles mail communication for you. Use that.
The error Use of uninitialized value $_ in concatenation (.) or string is exactly what it says. You are attempting to use a value of a variable that hasn't been set. In this case, the variable is the #_ variable in your foreach statement. Your foreach isn't a true foreach, but part of your print statement since your EOF is after your for statement. This looks like an error.
Also, what is the value of #_? This variable contains a list of values that have been passed to your subroutine. If none are passed, it will be undefined. Even if #_ is undefined, foreach (undef) will simply skip the loop. However, since foreach (#_) { is a string to print, your Perl program will crash without #_ being defined.
If you remove the -w from #!/usr/bin/perl, your program will actually "work" (Note the quotes), and you'll see that your foreach will literally print.
I do not recommend you not to use warnings which is what -w does. In fact, I recommend you use warnings; rather than -w. However, in this case, it might help you see your error.
You have EOF after the line with foreach. It contains $_ which is interpolated here but $_ is not initialized yet because it is not in foreach loop. It is not code but just text. Move EOF before foreach.
But probably you would like
sub MailNewFiles {
$mail= "sendmail";
open ($mail, "| /usr/lib/sendmail -oi -t" ) ||die "errors with sendmail $!"; # open handler and pipe it to sendmail
local $"="\n"; # " make syntax highlight happy
print $mail <<"EOF"; #print till the end of fiEOF
From: "user";
To: "root";
Subject: "New Files Found";
New files found:
#_
EOF
close($mail);
}
See perlvar for more informations about $".
The message
Use of uninitialized value $xxx in ...
is very straightforward. When you encounter it, it means that you are using a variable ($xxx) in any way, but that the variable has not ever been initialized.
Sometimes, adding an initialization command at the start of you code is enough :
my $str = '';
my $num = 0;
Sometimes, your algorithm is wrong, or you just mistyped your variable, like in :
my $foo = 'foo';
my $bar = $ffo . 'bar'; # << There is a warning on this line
# << because you made a mistake on $foo ($ffo)

2 Sub references as arguments in perl

I have perl function I dont what does it do?
my what does min in perl?
#ARVG what does mean?
sub getArgs
{
my $argCnt=0;
my %argH;
for my $arg (#ARGV)
{
if ($arg =~ /^-/) # insert this entry and the next in the hash table
{
$argH{$ARGV[$argCnt]} = $ARGV[$argCnt+1];
}
$argCnt++;
}
return %argH;}
Code like that makes David sad...
Here's a reformatted version of the code doing the indentations correctly. That makes it so much easier to read. I can easily tell where my if and loops start and end:
sub getArgs {
my $argCnt = 0;
my %argH;
for my $arg ( #ARGV ) {
if ( $arg =~ /^-/ ) { # insert this entry and the next in the hash table
$argH{ $ARGV[$argCnt] } = $ARGV[$argCnt+1];
}
$argCnt++;
}
return %argH;
}
The #ARGV is what is passed to the program. It is an array of all the arguments passed. For example, I have a program foo.pl, and I call it like this:
foo.pl one two three four five
In this case, $ARGV is set to the list of values ("one", "two", "three", "four", "five"). The name comes from a similar variable found in the C programming language.
The author is attempting to parse these arguments. For example:
foo.pl -this that -the other
would result in:
$arg{"-this"} = "that";
$arg{"-the"} = "other";
I don't see min. Do you mean my?
This is a wee bit of a complex discussion which would normally involve package variables vs. lexically scoped variables, and how Perl stores variables. To make things easier, I'm going to give you a sort-of incorrect, but technically wrong answer: If you use the (strict) pragma, and you should, you have to declare your variables with my before they can be used. For example, here's a simple two line program that's wrong. Can you see the error?
$name = "Bob";
print "Hello $Name, how are you?\n";
Note that when I set $name to "Bob", $name is with a lowercase n. But, I used $Name (upper case N) in my print statement. As it stands, now. Perl will print out "Hello, how are you?" without a care that I've used the wrong variable name. If it's hard to spot an error like this in a two line program, imagine what it would be like in a 1000 line program.
By using strict and forcing me to declare variables with my, Perl can catch that error:
use strict;
use warnings; # Another Pragma that should always be used
my $name = "Bob";
print "Hello $Name, how are you doing\n";
Now, when I run the program, I get the following error:
Global symbol "$Name" requires explicit package name at (line # of print statement)
This means that $Name isn't defined, and Perl points to where that error is.
When you define variables like this, they are in scope with in the block where it's defined. A block could be the code contained in a set of curly braces or a while, if, or for statement. If you define a variable with my outside of these, it's defined to the end of the file.
Thus, by using my, the variables are only defined inside this subroutine. And, the $arg variable is only defined in the for loop.
One more thing:
The person who wrote this should have used the Getopt::Long module. There's a major bug in their code:
For example:
foo.pl -this that -one -two
In this case, my hash looks like this:
$args{'-this'} = "that";
$args{'-one'} = "-two";
$args{'-two'} = undef;
If I did this:
if ( defined $args{'-two'} ) {
...
}
I would not execute the if statement.
Also:
foo.pl -this=that -one -two
would also fail.
#ARGV is a special variable (refer to perldoc perlvar):
#ARGV
The array #ARGV contains the command-line arguments intended for the
script. $#ARGV is generally the number of arguments minus one, because
$ARGV[0] is the first argument, not the program's command name itself.
See $0 for the command name.
Perl documentation is also available from your command line:
perldoc -v #ARGV

break out of a subroutine

what is the best way to break out of a subroutine & continue processing the rest of the script?
ie
#!/usr/bin/perl
use strict;
use warnings;
&mySub;
print "we executed the sub partway through & continued w/ the rest
of the script...yipee!\n";
sub mySub{
print "entered sub\n";
#### Options
#exit; # will kill the script...we don't want to use exit
#next; # perldoc says not to use this to breakout of a sub
#last; # perldoc says not to use this to breakout of a sub
#any other options????
print "we should NOT see this\n";
}
At the expense of stating the obvious the best way of returning for a subroutine is ......
return
Unless there is some hidden subtlety in the question that isn't made clear
Edit - maybe I see what you are getting at
If you write a loop, then a valid way of getting out of the loop is to use last
use strict ;
use warnings ;
while (<>) {
last if /getout/ ;
do_something() ;
}
If you refactor this, you might end up with a using last to get out of the subroutine.
use strict ;
use warnings ;
while (<>) {
process_line() ;
do_something() ;
}
sub process_line {
last if /getout/ ;
print "continuing \n" ;
}
This means you are using last where you should be using return and if you have wanings in place you get the error :
Exiting subroutine via last at ..... some file ...
Don't use exit to abort a subroutine if there's any chance that someone might want to trap whatever error happened. Use die instead, which can be trapped by an eval.

In Perl, how do I process input as soon as it arrives, instead of waiting for newline?

I'd like to run a subcommand from Perl (or pipe it into a Perl script) and have the script process the command's output immediately, rather than waiting for a timeout, a newline, or a certain number of blocks. For example, let's say I want to surround each chunk of input with square brackets. When I run the script like this:
$ ( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz) | my_script.pl
I'd like the output to be this, with each line appearing five seconds after the previous one:
[foo]
[bar]
[baz]
How do I do that?
This works, but is really ugly:
#! /usr/bin/perl -w
use strict;
use Fcntl;
my $flags = '';
fcntl(STDIN, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl(STDIN, F_SETFL, $flags);
my $rin = '';
vec($rin,fileno(STDIN),1) = 1;
my $rout;
while (1) {
select($rout=$rin, undef, undef, undef);
last if eof();
my $buffer = '';
while (my $c = getc()) {
$buffer .= $c;
}
print "[$buffer]\n";
}
Is there a more elegant way to do it?
From perlfaq5: How can I read a single character from a file? From the keyboard?. You probably also want to read How can I tell whether there's a character waiting on a filehandle?. Poll the filehandle. If there is a character there, read it and reset a timer. If there is not character there, try again. If you've retried and passed a certain time, process the input.
After you read the characters, it's up to you to decide what to do with them. With all the flexibility of reading single characters comes the extra work of handling them.
Term::ReadKey can do this for you. In particular setting the ReadKey() mode to do the polling for you.
use Term::ReadKey;
$| = 1;
while( my $key = ReadKey(10) ) {
print $key;
}
If there's time inbetween each character, you might be able to detect the pauses.
Perl also does line input - if you don't use getc you should be able to add newlines to the end of foo, bar, etc and perl will give you each line.
If you can't add newlines, and you can't depend on a pause, then what exactly do you expect the system to do to tell perl that it's started a new command? As far as perl is concerned, there's a stdin pipe, it's eating data from it, and there's nothing in the stdin pipe to tell you when you are executing a new command.
You might consider the following instead:
$ echo "( echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz)" | my_script.pl
or
$ my_script.pl$ "echo -n foo ; sleep 5 ; echo -n bar ; sleep 5; echo baz"
And modify your perl program to parse the input "command line" and execute each task, eating the stdout as needed.
-Adam
See How to change Open2 input buffering. (Basically, you have to make the other program think it's talking to a tty.)
You didn't mention how you are reading input in your Perl script, but you might want to look at the getc function:
$|++; # set autoflush on output
while ($c = getc(STDIN)) {
print $c;
}