views:

833

answers:

4

Hi,

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.

+1  A: 
Neeraj
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
+1  A: 

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
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
IO::File didn't seem to change anything (see first post edit) :(
Isaac Clarke
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
A: 

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
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
Don't be hard on yourself - we have all done it........just don't do it again 8-)!!!
Beano
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
Maybe you can update the question to note this. It's not an uncommon experience you've had, though. :)
brian d foy
Posted the full subroutine code ;)
Isaac Clarke
+1  A: 

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
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
Posted the full subroutine code ;)
Isaac Clarke
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
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
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
Sorry I didn't see your comments earlier, but yeah, that's basically what I wanted to do ! Thanks !
Isaac Clarke