Perl hangs up on while loop - perl

This code hangs up for some reason or just doesn't go any further when while (<>) { $file .= $_}; is queried. Why is that?
As soon as I start the code with the entered text does not happen more than that it outputs task1 and then it hangs.
Code:
#!/usr/bin/perl -w
use strict;
use JSON;
my $json = JSON->new->allow_nonref;
my $file = "";
print('task1');
while (<>) { $file .= $_ };
print('task2');
my $json_output = $json->decode( $file );
my ($c, $i, $cstr, $istr);
foreach my $cert (#$json_output) {
print('task3');
$i = $json_output->{i};
$c = $json_output->{c};
$istr = join("", map { sprintf("%02x",$_) } #$i);
$cstr = pack("C*", #$c);
open(F, ">$istr.der"); print F $cstr; close(F);
print('done.');
}
Output:
task1

This line
while (<>) { $file .= $_ };
is trying to read from a file specified on the command line, or if there isn't one, from standard input. If there isn't anything piped to standard input, then it sits waiting for you to type something at the keyboard.
So I'm guessing you didn't specify a file on the command line, and your program is sitting there waiting to get input from standard input.
Also, the easier way to read in the entire file to a single variable is like so:
my $file = do { local $/; <> };
See this article for other options.

How do you invoke your code? The <> operator means that it takes input from either all the files that you specify as arguments, or from standard input. If you call your script with no arguments, it will sit and wait for console input.
If you call it without arguments, try entering a few lines of text when it is "hanging", and then type Ctrl+D if you are on Linux, or Ctrl+Z on Windows. That should make the script work.

Related

zcat working in command line but not in perl script

Here is a part of my script:
foreach $i ( #contact_list ) {
print "$i\n";
$e = "zcat $file_list2| grep $i";
print "$e\n";
$f = qx($e);
print "$f";
}
$e prints properly but $f gives a blank line even when $file_list2 has a match for $i.
Can anyone tell me why?
Always is better to use Perl's grep instead of using pipe :
#lines = `zcat $file_list2`; # move output of zcat to array
die('zcat error') if ($?); # will exit script with error if zcat is problem
# chomp(#lines) # this will remove "\n" from each line
foreach $i ( #contact_list ) {
print "$i\n";
#ar = grep (/$i/, #lines);
print #ar;
# print join("\n",#ar)."\n"; # in case of using chomp
}
Best solution is not calling zcat, but using zlib library :
http://perldoc.perl.org/IO/Zlib.html
use IO::Zlib;
# ....
# place your defiiniton of $file_list2 and #contact list here.
# ...
$fh = new IO::Zlib; $fh->open($file_list2, "rb")
or die("Cannot open $file_list2");
#lines = <$fh>;
$fh->close;
#chomp(#lines); #remove "\n" symbols from lines
foreach $i ( #contact_list ) {
print "$i\n";
#ar = grep (/$i/, #lines);
print (#ar);
# print join("\n",#ar)."\n"; #in case of using chomp
}
Your question leaves us guessing about many things, but a better overall approach would seem to be opening the file just once, and processing each line in Perl itself.
open(F, "zcat $file_list |") or die "$0: could not zcat: $!\n";
LINE:
while (<F>) {
######## FIXME: this could be optimized a great deal still
foreach my $i (#contact_list) {
if (m/$i/) {
print $_;
next LINE;
}
}
}
close (F);
If you want to squeeze out more from the inner loop, compile the regexes from #contact_list into a separate array before the loop, or perhaps combine them into a single regex if all you care about is whether one of them matched. If, on the other hand, you want to print all matches for one pattern only at the end when you know what they are, collect matches into one array per search expression, then loop them and print when you have grepped the whole set of input files.
Your problem is not reproducible without information about what's in $i, but I can guess that it contains some shell metacharacter which causes it to be processed by the shell before the grep runs.

How do I reset data from STDIN when I am finished with it?

I have a program while gets data from a pipe of the contents of a test file (cat file). I typically use the diamond operator to get the data line by line from STDIN, till the last line. Trouble is that I would like to reuse the same data for more than one subroutine. How is it possible to reset reading of STDIN so that the data can be read again from the first line?
sub downloadsrt {
print "Printing list of subtitle files in downloadable form..\n";
while (<>) {
chomp($_);
(my $fname,my $path, my $suffix) = fileparse($_);
$_=$fname;
my ($name, $ext) = $fname =~ /(.*)\.(.*)/;
#For srt
my $newfile=$path.$name.".$ext";
$newfile =~ s/\s/%20/g;
$newfile =~ s/\/root/http:\/\/$localip/;
print $newfile."\n";
}
}
sub dummysub {
while (<>) {
// Something else
}
}
downloadsrt;
dummysub;
I would like to access STDIN using multiple subroutines, and each should get data from the first line of STDIN.
Unless I am mistaken this is not lexically scoped.
As suggested by #mpapec, you need to store the data from STDIN into an array:
my #stdin = <>;
mysub_1(\#stdin);
mysub_2(\#stdin);

Perl - Using backquotes missing output

Hello guys i need to capture the output of an external command, herefore I use backquotes.
However when the command reaches a newline the output is ommitted. Where $_ = AD
#lines = `"C:/Program Files/Veritas/NetBackup/bin/admincmd/bppllist" $_ -U"`
Test: test1
Test: test2
Test: test3
Test: test4
The actual output:
#lines
Test: test1
Test: test2
Thank you for your time.
print HTML "<h2 id='pol'>Policy Configuration\n</h2>" ;
#bpllist =`"$admincmd/bppllist.exe"` or die print "$admincmd/bppllist.exe not found or could not be executed";
foreach (#bpllist)
{
print HTML "<div><table class='table'>\n";
#lines = `"$admincmd/bppllist" $_ -U` or die print "$admincmd/bpplinfo $_ -U not found or could not be executed";
print HTML "\t<tr>\n\t<td><b>Policy name: <b></td><td>$_</td>\n\t</tr>\n" ;
foreach (#lines) {
chop;
($var, $value) = split(/:/,$_,2);
$var = "" if !defined($var);
$value = "" if !defined($value);
print HTML "\t<tr>\n\t<td>$var</td><td>$value</td>\n\t</tr>\n" ;
}
print HTML "</table></div>";
}
The output of #bpllist:
AD
Sharepoint
Echchange
Vmware
Here's how to capture the STDOUT & STDERR of a spawned process using backticks:
my $output = join('', `command arg1 arg2 arg3 2>&1`);
How it works has no dependence whatsoever on newlines in the output of command.
If you also need to send text to command's STDIN, then use IPC::Open3.
Cleaned your code up a bit. It works for me.
use strict;
use warnings;
use 5.10.0;
# something missing here to set up HTML file handle
# something missing here to set up $admincmd
print HTML q{<h2 id='pol'>Policy Configuration\n</h2>};
my #bpllist = `"$admincmd/bppllist.exe"`
or die "$admincmd/bppllist.exe not found or could not be executed\n";
for my $policy (#bpllist) {
print HTML q{<div><table class='table'>\n};
my #lines = `$admincmd/bpplinfo.exe $policy -U 2>&1`;
print HTML qq{\t<tr>\n\t<td><b>Policy name: <b></td><td>$policy</td>\n\t</tr>\n} ;
for my $pair (#lines) {
chomp($pair); # only remove newlines, not other characters
my ($var, $value) = split /:/, $pair, 2;
$var //= '';
$value //= '';
print HTML qq{\t<tr>\n\t<td>$var</td><td>$value</td>\n\t</tr>\n} ;
}
print HTML q{</table></div>};
}
Update 2
You appear to be doing this on windows?
I don't think the 2>&1 trick will work there.
Instead of using qx or backticks and then shell commands to redirect output, give the core module, IPC::Cmd, a try. In particular, its exportable function &run will conveniently capture both STDOUT and STDERR for you. From the synopsis:
### in list context ###
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) =
run( command => $cmd, verbose => 0 );
Maybe the command send its output to stderr.
Try this:
my $output = `'command' -ARG -L 2>&1`;
regards,

Programmatically read from STDIN or input file in Perl

What is the slickest way to programatically read from stdin or an input file (if provided) in Perl?
while (<>) {
print;
}
will read either from a file specified on the command line or from stdin if no file is given
If you are required this loop construction in command line, then you may use -n option:
$ perl -ne 'print;'
Here you just put code between {} from first example into '' in second
This provides a named variable to work with:
foreach my $line ( <STDIN> ) {
chomp( $line );
print "$line\n";
}
To read a file, pipe it in like this:
program.pl < inputfile
The "slickest" way in certain situations is to take advantage of the -n switch. It implicitly wraps your code with a while(<>) loop and handles the input flexibly.
In slickestWay.pl:
#!/usr/bin/perl -n
BEGIN: {
# do something once here
}
# implement logic for a single line of input
print $result;
At the command line:
chmod +x slickestWay.pl
Now, depending on your input do one of the following:
Wait for user input
./slickestWay.pl
Read from file(s) named in arguments (no redirection required)
./slickestWay.pl input.txt
./slickestWay.pl input.txt moreInput.txt
Use a pipe
someOtherScript | ./slickestWay.pl
The BEGIN block is necessary if you need to initialize some kind of object-oriented interface, such as Text::CSV or some such, which you can add to the shebang with -M.
-l and -p are also your friends.
You need to use <> operator:
while (<>) {
print $_; # or simply "print;"
}
Which can be compacted to:
print while (<>);
Arbitrary file:
open my $F, "<file.txt" or die $!;
while (<$F>) {
print $_;
}
close $F;
If there is a reason you can't use the simple solution provided by ennuikiller above, then you will have to use Typeglobs to manipulate file handles. This is way more work. This example copies from the file in $ARGV[0] to that in $ARGV[1]. It defaults to STDIN and STDOUT respectively if files are not specified.
use English;
my $in;
my $out;
if ($#ARGV >= 0){
unless (open($in, "<", $ARGV[0])){
die "could not open $ARGV[0] for reading.";
}
}
else {
$in = *STDIN;
}
if ($#ARGV >= 1){
unless (open($out, ">", $ARGV[1])){
die "could not open $ARGV[1] for writing.";
}
}
else {
$out = *STDOUT;
}
while ($_ = <$in>){
$out->print($_);
}
Do
$userinput = <STDIN>; #read stdin and put it in $userinput
chomp ($userinput); #cut the return / line feed character
if you want to read just one line
Here is how I made a script that could take either command line inputs or have a text file redirected.
if ($#ARGV < 1) {
#ARGV = ();
#ARGV = <>;
chomp(#ARGV);
}
This will reassign the contents of the file to #ARGV, from there you just process #ARGV as if someone was including command line options.
WARNING
If no file is redirected, the program will sit their idle because it is waiting for input from STDIN.
I have not figured out a way to detect if a file is being redirected in yet to eliminate the STDIN issue.
if(my $file = shift) { # if file is specified, read from that
open(my $fh, '<', $file) or die($!);
while(my $line = <$fh>) {
print $line;
}
}
else { # otherwise, read from STDIN
print while(<>);
}

How do I get the output of an external command in Perl?

I want to have output of Windows command-line program (say, powercfg -l) written into a file which is created using Perl and then read the file line by line in a for loop and assign it to a string.
You have some good answers already. In addition, if you just want to process a command's output and don't need to send that output directly to a file, you can establish a pipe between the command and your Perl script.
use strict;
use warnings;
open(my $fh, '-|', 'powercfg -l') or die $!;
while (my $line = <$fh>) {
# Do stuff with each $line.
}
system 'powercfg', '-l';
is the recommended way. If you don't mind spawning a subshell,
system "powercfg -l";
will work, too. And if you want the results in a string:
my $str = `powercfg -l`;
my $output = qx(powercfg -l);
## You've got your output loaded into the $output variable.
## Still want to write it to a file?
open my $OUTPUT, '>', 'output.txt' or die "Couldn't open output.txt: $!\n";
print $OUTPUT $output;
close $OUTPUT
## Now you can loop through each line and
## parse the $line variable to extract the info you are looking for.
foreach my $line (split /[\r\n]+/, $output) {
## Regular expression magic to grab what you want
}
There is no need to first save the output of the command in a file:
my $output = `powercfg -l`;
See qx// in Quote-Like Operators.
However, if you do want to first save the output in a file, then you can use:
my $output_file = 'output.txt';
system "powercfg -l > $output_file";
open my $fh, '<', $output_file
or die "Cannot open '$output_file' for reading: $!";
while ( my $line = <$fh> ) {
# process lines
}
close $fh;
See perldoc -f system.
Since the OP is running powercfg, s/he are probably capturing the ouput of the external script, so s/he probably won't find this answer terribly useful. This post is primarily is written for other people who find the answers here by searching.
This answer describes several ways to start command that will run in the background without blocking further execution of your script.
Take a look at the perlport entry for system. You can use system( 1, 'command line to run'); to spawn a child process and continue your script.
This is really very handy, but there is one serious caveat that is not documented. If you start more 64 processes in one execution of the script, your attempts to run additional programs will fail.
I have verified this to be the case with Windows XP and ActivePerl 5.6 and 5.8. I have not tested this with Vista or with Stawberry Perl, or any version of 5.10.
Here's a one liner you can use to test your perl for this problem:
C:\>perl -e "for (1..100) { print qq'\n $_\n-------\n'; system( 1, 'echo worked' ), sleep 1 }
If the problem exists on your system, and you will be starting many programs, you can use the Win32::Process module to manage your application startup.
Here's an example of using Win32::Process:
use strict;
use warnings;
use Win32::Process;
if( my $pid = start_foo_bar_baz() ) {
print "Started with $pid";
}
:w
sub start_foo_bar_baz {
my $process_object; # Call to Create will populate this value.
my $command = 'C:/foo/bar/baz.exe'; # FULL PATH to executable.
my $command_line = join ' ',
'baz', # Name of executable as would appear on command line
'arg1', # other args
'arg2';
# iflags - controls whether process will inherit parent handles, console, etc.
my $inherit_flags = DETACHED_PROCESS;
# cflags - Process creation flags.
my $creation_flags = NORMAL_PRIORITY_CLASS;
# Path of process working directory
my $working_directory = 'C:/Foo/Bar';
my $ok = Win32::Process::Create(
$process_object,
$command,
$command_line,
$inherit_flags,
$creation_flags,
$working_directory,
);
my $pid;
if ( $ok ) {
$pid = $wpc->GetProcessID;
}
else {
warn "Unable to create process: "
. Win32::FormatMessage( Win32::GetLastError() )
;
return;
}
return $pid;
}
To expand on Sinan's excellent answer and to more explicitly answer your question:
NOTE: backticks `` tell Perl to execute a command and retrieve its output:
#!/usr/bin/perl -w
use strict;
my #output = `powercfg -l`;
chomp(#output); # removes newlines
my $linecounter = 0;
my $combined_line;
foreach my $line(#output){
print $linecounter++.")";
print $line."\n"; #prints line by line
$combined_line .= $line; # build a single string with all lines
# The line above is the same as writing:
# $combined_line = $combined_line.$line;
}
print "\n";
print "This is all on one line:\n";
print ">".$combined_line."<";
Your output (on my system) would be:
0)
1)Existing Power Schemes (* Active)
2)-----------------------------------
3)Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced) *
4)Power Scheme GUID: 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c (High performance)
5)Power Scheme GUID: a1841308-3541-4fab-bc81-f71556f20b4a (Power saver)
This is all on one line:
>Existing Power Schemes (* Active)-----------------------------------Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced) *Power Scheme GUID: 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c (High performance)Power Scheme GUID: a1841308-3541-4fab-bc81-f71556f20b4a (Power saver)<
Perl makes it easy!
Try using > operator to forward the output to a file, like:
powercfg -l > output.txt
And then open output.txt and process it.