3

I'm trying to read from two files, and generate output in a third. I first wanted to edit the first one on the go but I didn't find a suitable method save for arrays.

My problem is that the third file (output) is empty whenever I uncomment the "_ref_param_handling" function. BUT the following is what puzzles me the most: If I do a UNIX very basic `cat` system call on the output file at then end (see code below), it works just fine. If I open the filehandle just before and close it right after editing, it also works fine (around my print FILEHANDLE LIST).

I undoubtedly am missing something here. Apart from a problem between my keyboard and my chair, what is it? A filehandle conflict? A scope problem?

Every variable is declared and has the value I want it to have.

Edit (not applicable anymore). Using IO::File on the three files didn't change anything.


Edit 2 : New full subroutine code

My code works (except when my ref already exists, but that's because of the "append" mode i think) but there might be some mistakes and unperlish ways of coding (sorry, Monks). I, however, use Strict and warnings !

sub _ref_edit($) {
    my $manda_def = "$dir/manda_def.list";
    my $newrefhandle;
    my $ref       = $_[0];
    (my $refout   = $ref) =~ s/empty//;
    my $refhandle;
    my $parname   = '';
    my $parvalue  = '';
    my @val;

    _printMan;

    my $flush = readline STDIN;    # Wait for <enter>

    # If one or both of the ref. and the default values are missing
    if ( !( -e $manda_def && -e $ref ) ) {
        die "Cannot find $ref and/or $manda_def";
    }

    # Open needed files (ref & default)
    open( $refhandle, "<", $ref ) or die "Cannot open ref $ref : $!";
    open( $newrefhandle, ">>", $refout ) 
      or die "Cannot open new ref $refout : $!";

    # Read each line
    while ( my $refline = <$refhandle> ) {
    # If line read not an editable macro
        if ( $refline =~ /^define\({{(.+)}},\s+{{.*__VALUE__.*}}\)/ ){
        $parname = $1;         # $1 = parameter name captured in regexp
        # Prompt user
        $parvalue = _ref_param_handling( $parname, $manda_def );   
        # Substitution in ref
        $refline =~ s/__VALUE__/$parvalue/;
        # Param not specified and no default value
        $parvalue eq '' ? $refline=~s/__COM__/#/ : $refline=~s/__COM__//; 
        }

    print $newrefhandle $refline;
    }
    close $newrefhandle;
    close $refhandle;

    return $refout;
}    # End ref edit  

the _ref_param_handle subroutine still is :

open( $mde, '<', $_[1] )
      or die "Cannot open mandatory/default list $_[1] : $!";

    # Read default/mandatory file list 
    while (<$mde>) {       
       ( $name, $manda, $default, $match, $descript ) = split( /\s+/, $_, 5 ); 
       next if ( $name !~ $ref_param );  # If param read differs from parname

    (SOME IF/ELSE)

    } # End while <MDE>
    close $mde;
    return $input;
}

Extract from manda_def file :

NAME  Mandatory? Default Match      Comm.
PORT          y NULL  ^\d+$ Database port
PROJECT       y NULL  \w{1,5}   Project name
SERVER        y NULL  \w+           Server name
modemRouting  n NULL  .+        
modlib        y bin   .+        
modules       y sms   .+

Extract from ref_file :

define({{PORT}},         {{__VALUE__}})dnl
define({{PROJECT}},      {{__VALUE__}})dnl
define({{SERVER}},       {{__VALUE__}})dnl
define({{modemRouting}}, {{__COM__{{$0}} '__VALUE__'}})dnl
define({{modlib}},       {{__COM__{{$0}} '__VALUE__'}})dnl
define({{modules}},      {{__COM__{{$0}} '__VALUE__'}})dnl

Any help appreciated.

Jens
  • 69,818
  • 15
  • 125
  • 179
Isaac Clarke
  • 717
  • 1
  • 9
  • 19
  • **Always** start your Perl programs with "`use strict;`" and "`use warnings'`". – Brad Gilbert Jul 29 '09 at 15:35
  • I do ! This only is a part of a subroutine. – Isaac Clarke Jul 29 '09 at 15:38
  • 1
    I *think* I understand what you mean to do. Still don't know if you want all those {{}} replaced, thought. Anyways, you say this already works, except for '>>'? What error do you get? – Leonardo Herrera Aug 03 '09 at 15:10
  • Hey Leonardo, thanks for your reply. I don't want any of my `{{' or `}}' replaced, they're my m4 comment-delimiters. I actually get an infinite writing loop when accessing an existing ref_file with '>>' but don't waste time on this. I'm calling $EDITOR (or vi) instead of prompting line-by-line (only when specifying an existing ref_file). This way, the user can modify whatever he needs. – Isaac Clarke Aug 04 '09 at 08:16

4 Answers4

1

try opening the second file handle for input outside the loop and pass a reference to the subroutine _ref_param_handle.Use seek function to seek file back to start.
If your file is not too large you can also think of storing the content in an array and the accessing it instead of looping over same contents.
EDIT:
Here is a small example to support what I was trying to say above:


#!/usr/bin/perl -w

sub test
{
 my $fh_to_read = $_[0] ;
 my $fh_to_write = $_[1] ;

 while(<$fh_to_read>)
 {
  print $fh_to_write  $_ ;
 }
 seek($fh_to_read,0,0) ;
}

open(FH1,"<dummy1");
open(FH2,"<dummy2");
open(FH3,">dummy3");

while(<FH2>)
{
 print FH3 "$_" ;
 test(\*FH1,\*FH3);
}

Info about perl references

sud03r
  • 19,109
  • 16
  • 77
  • 96
  • Thanks for your answer. I'm not familiar with perl's references (having difficultie distinguishing C++'s pointers/references with Perl's Hard/Soft ref). I tried _ref_param_handling($\$) but passing \$mde to my function returns an error. Is there a trick (or an explanation... :) ) – Isaac Clarke Jul 29 '09 at 08:55
1

It is unclear what is initialising $refhandle, $newrefhandle and $mde. Depending on the values they have will affect the behaviour of open - i.e. whether it will close any filehandles before opening a new one.

I would suggest that you start using the IO::File interface to open/write to files, as this makes the job of filehandle management much easier, and will avoid any inadvertent closes. Something like...

use IO::File;

my $refhandle = IO::File->new("< $ref") or die "open() - $!";

$refhandle->print(...);

As far as editing files in place goes, this is a common pattern I use to achieve this, make sure of the -i behaviour of perl.

sub edit_file
{
    my ($filename) = @_;

    # you can re-create the one-liner above by localizing @ARGV as the list of
    # files the <> will process, and localizing $^I as the name of the backup file.
    local (@ARGV) = ($filename);
    local($^I) = '.bak';

    while (<>)
    {
        s/original string/new string/g;
    }
    continue
    {
        print;
    }
}
Beano
  • 7,551
  • 3
  • 24
  • 27
  • OK, thanks. Fortunately, IO::File seems to be included with Perl's standard delivery (I may not install any package on the machine I will be working on). About my three variables, they're just declared as "my _;". Is that bad ? – Isaac Clarke Jul 29 '09 at 09:09
  • IO::File didn't seem to change anything (see first post edit) :( – Isaac Clarke Jul 29 '09 at 09:32
  • Declaring filehandles just as my is not bad - according to the documentation, undefined scalar variables are assigned a reference to a new anonymous filehandle. – Beano Jul 29 '09 at 10:05
1

From what I gather, your script wants to convert a file in the following form:

define({{VAR1}}, {{__VALUE__}})
define({{VAR2}}, {{__VALUE__}})
define({{VAR3}}, {{__VALUE__}})
define({{VAR4}}, {{__VALUE__}})

to something like this:

define({{VAR1}}, {{}})
define({{VAR2}}, {{VALUE2}})
define({{VAR3}}, {{VALUE3}})
define({{VAR4}}, {{}})

The following works. I don't know what manda_def means, and also I didn't bother to create an actual variable replacement function.

#!/usr/bin/perl
use strict;
use warnings;

sub work {
    my ($ref, $newref, $manda_def) = @_;

    # Open needed files (ref & default)
    open(my $refhandle, '<', $ref) or die "Cannot open ref $ref : $!";
    open(my $newrefhandle, '>', $newref) or die "Cannot open new ref $newref: $!";

    # Read each line
    while (my $refline = <$refhandle>) {
        # if line read is not an editable macro
        if ($refline =~ /^define\({{(.+)}},\s+{{.*__VALUE__.*}}\)/){
            my $parvalue = _ref_param_handling($1, $manda_def); # manda_def?
            # Substitution in ref
            $refline  =~ s/__VALUE__/$parvalue/;
            # Param not specified and no default value
            $refline  =~ s/__COM__/#/ if $parvalue eq '';
        }
        print $newrefhandle $refline;
    }
    close $newrefhandle;
    close $refhandle;

    return $newref;
}

sub _ref_param_handling {
    my %parms = (VAR2 => 'VALUE2', VAR3 => 'VALUE3');
    return $parms{$_[0]} if exists $parms{$_[0]};
}

work('ref.txt', 'newref.txt', 'manda.txt');
Leonardo Herrera
  • 8,388
  • 5
  • 36
  • 66
  • In fact it's a little more complicated than that. $manda_def is a file path containing a list of default values for the parameters, saying if they're mandatory or not and giving a match pattern for later user input. \_ref\_param\_handling *opens this file through another while/filehandle (which - i think - conflicts with the two other) and asks user to enter the parameter. I will post some more samples tomorrow morning, if you still want to help ;) – Isaac Clarke Jul 29 '09 at 15:47
  • Two things. If you fixed your program based on any answer, you should mark it as such. Second, what do you think caused your problem in the first place? – Leonardo Herrera Aug 03 '09 at 15:45
  • Nope, I didn't fix anything at that time, I just posted the whole code. As I said, my script never failed, the writing was just done at the end (when "close" occured) ;) My problem is somewhat solved, then. – Isaac Clarke Aug 04 '09 at 08:20
  • 1
    Oh -- do you mean you just wanted to check your progress during execution? Then you should flush your handles. The easier way is to just add "$| = 1" at the beginning of your script. – Leonardo Herrera Aug 04 '09 at 16:13
  • Sorry I didn't see your comments earlier, but yeah, that's basically what I wanted to do ! Thanks ! – Isaac Clarke Aug 11 '09 at 08:43
0

Guys, I seriously consider hanging myself with my wireless mouse.

My script never failed. I just didn't ran it through the end (it's actually a very long parameter list). The printing is just done as soon as the filehandle is closed (or so I guessed)...

/me *cries*

I've spent 24 hours on this...

Isaac Clarke
  • 717
  • 1
  • 9
  • 19
  • 1
    Isaac, can you put the actual code in use? Not a reduced snippet, but the whole function. I think your problem is happening somewhere else. Have you put "use strict" and "use warnings" on top of your program? Are you using small scopes for variables? Also, a small sample of your "ref" file will be useful to help you. – Leonardo Herrera Jul 29 '09 at 15:13
  • 1
    Don't be hard on yourself - we have all done it........just don't do it again 8-)!!! – Beano Jul 29 '09 at 15:19
  • I'll post everything first hour in the morning, I'm away from my dev computer at the moment. And of course, I use Warnings and Strict. Perl's too tricky without them. About the scope, I'm alaways using *my* (only 'our' variable is for GetOpts::Std), even if it confuses me sometimes when I read about it on the web and/or Perl books. – Isaac Clarke Jul 29 '09 at 15:51
  • Maybe you can update the question to note this. It's not an uncommon experience you've had, though. :) – brian d foy Jul 29 '09 at 18:24