Win has asked for the wisdom of the Perl Monks concerning the following question:

I can not see what is wrong with this code. The error messages comes back as:

syntax error at pull_out_tables.pl line 24, near "if $line " Can't use global $1 in "my" at pull_out_tables.pl line 30, near "= $1" syntax error at pull_out_tables.pl line 35, near "}"
#! perl -w scipt use strict; use warnings; my @files; my @List_of_commands; my $line; my $file; while (<*.sql>) { push (@files,$_); } foreach (@files) { $file = $_; open (FILE, "<$file"); while (<FILE>){ my $line = $_; if $line not =~ (/INSERT INTO Message_pool)/{ #Do nothing } elsif $line =~ (/^.{0,50}CREATE TABLE\s([A-Z|a-z|_]{5,40})\s.{1,10 +0}/) { my $table_name; $table_name = $1; my $Command = "if exists (select 1 from INFORMATION_SCHEMA.table +s where table_name = '".$table_name."') DROP TABLE ".$table_name.""; push(@List_of_commands, $Command); } } } my $outfile = "Outfile.txt"; open (OUTFILE, "+>$outfile"); foreach (@List_of_commands){ print OUTFILE "$_\n" }

Replies are listed 'Best First'.
Re: if expression
by Corion (Patriarch) on Sep 16, 2005 at 14:10 UTC

    Let's just look at the first error that Perl tells you; usually the following errors are results of the first error:

    syntax error at pull_out_tables.pl line 24, near "if $line "

    Now, let's look at line 24:

    if $line not =~ (/INSERT INTO Message_pool)/{

    Now, I can propably guess what you want to say here, but Perl doesn't. There is a Mark-Jason Dominus quote in here, but I'll limit myself to point you to perldoc perlop. Perl isn't that good at guessing what you want. Likely you want the following code instead:

    if ($line !~ /INSERT INTO Message_pool/) {

    You should also try to reduce your scripts to the minimum snippet that fails instead of posting a whole program. I also noticed you talking in the CB about this earlier, and people already told you how the negation regex operator works. Why didn't you listen to them?

Re: if expression
by blazar (Canon) on Sep 16, 2005 at 14:34 UTC
    #! perl -w scipt
    Do you really want that shebang line?!?
    use strict; use warnings;
    Good! But then no need for -w.
    my @files; my @List_of_commands; my $line; my $file;
    Not that bad, but in Perl one seldom really needs to predeclare a long list of variables; it's better to declare them as close as possible to where they're actually needed.
    while (<*.sql>) { push (@files,$_); }
    Huh?!? Why not
    my @files=<*.sql>;
    foreach (@files) {
    So why not avoiding to use the intermediate variable @files and just do
    for (<*.sql>) { # I never use C<foreach> ;-)
    $file = $_;
    So why not limiting $file to this this lexical scope? Also it would be better to
    • either use $_ directly,
    • use the common perlish syntactic sugar:
      for my $file (<*.sql>) { # I still prefer C<for> orver C<foreach>!!
    open (FILE, "<$file");
    Didn't mummy told you to
    1. always check the return value of opens and (somewhat less importantly) also to
    2. use lexical filehandles and
    3. the 3-args form of open?
    while (<FILE>){ my $line = $_;
    Ditto as above!!
    if $line not =~ (/INSERT INTO Message_pool)/{ #Do nothing }
    Hmmm sounds Perl6-ish. No, let me see better... oh, it's just a misplaced parenthesis. No, let me see better... they're two misplaced parens. Seems we've found the problem.

    Incidentally, if #Do nothing is just a placeholder, then fine. But if you think you want to keep it like that, which is not totally unprobable, taking into account your coding {style,experience} as of what I could see above, then just know that eliminating the whole if statement will do just fine.

    All in all I recommend reading some introductory Perl tutorial before going on...

    Update: I gave a more comprehensive peek into the rest of your script and I think that it could be rewritten much more simply e.g. along this way:

    #!/usr/bin/perl # UNTESTED! use strict; use warnings; open my $out, '>', "outfile.txt" or die "Can't open output file: $!\n"; select $out; @ARGV=<*.sql>; while (<>) { next unless /^.{0,50}CREATE TABLE \s ([A-Z|a-z|_]{5,40}) \s .{1,100}/x; print "if exists (select 1 from INFORMATION_SCHEMA.tables where ta +ble_name = '$1') DROP TABLE '$1'\n"; } __END__
    Please note that I tried to keep as close as possible to your logic, but if it were just this functionality that you need, than I just wouldn't open the output file myself and use shell redirection instead. If this is to evolve into something bigger, then plainly ignore this comment! If it is not, then I would leave to the user to specify input and output on the cmd line:
    #!/usr/bin/perl -ln use strict; use warnings; BEGIN {@ARGV=map glob, @ARGV} # You're under Win*, aren't you? next unless /$whatever (\w+) $whatever/; print "whatever: $1"; __END__
      #! perl -w scipt use strict; use warnings;

      Good! But then no need for -w.

      Actually, -w and use warnings are scoped differently. -w is dynamically scoped (i.e. it's a global), while use warnings is file/block scoped. In other words, -w affects modules, but use warnings only affects the file its in.

      "scipt" should not be there, however.


      So why not avoiding to use the intermediate variable @files and just do

      for (<*.sql>) { # I never use C<foreach> ;-)

      Why load the entire list into memory? Just use:

      while (<*.sql>) { ...

      # I still prefer C<for> orver C<foreach>!!

      I use for for for loops (e.g. for (1..100)) and foreach for foreach loops (e.g. foreach (@array)). Using for unconditionally as you do is bad programming, since it affects the ability to read your program negatively.

        Actually, -w and use warnings are scoped differently. -w is dynamically scoped (i.e. it's a global), while use warnings is file/block scoped. In other words, -w affects modules, but use warnings only affects the file its in.
        Indeed. The point being that since he has use warnings; anyway, he most probably doesn't want -w on the shebang line, leaving aside "scipt", for the moment. All in all that looked much like cargo cult to me...
        for (<*.sql>) { # I never use C<foreach> ;-)
        Why load the entire list into memory? Just use:
        Oh, how many *.sql files can he have?!? But jokes apart, I second that: while is certainly better, well: until we'll have lazy evaluation!!
        while (<*.sql>) { ...
        In fact. Except that, as a minor note, you'd probably need to give it a name. And as an even minor note, to avoid confusion with the typical filehandle iterator idiom, I'd write an explicit glob there.
        I use for for for loops (e.g. for (1..100)) and foreach for foreach loops (e.g. foreach (@array)). Using for unconditionally as you do is bad programming, since it affects the ability to read your program negatively.
        I beg to differ: I find both quite as readable and synonims also in the psychological feeling they give. And I find for to be easier both to think of and to type. Using foreach would affect negatively readability for me and avoiding to use it IMHO doesn't severly injure it for others. As many other things in Perl, it's largely a matter of personal tastes; I wouldn't be so drastic about it. Of course I'm not trying or even thinking of imposing my own point of view, either. Period!
        A reply falls below the community's threshold of quality. You may see it by logging in.
Re: if expression
by goldclaw (Scribe) on Sep 16, 2005 at 14:09 UTC
    Heres a hint, whats wrong with the paranthesis and /'s on this line:
    if $line not =~ (/INSERT INTO Message_pool)/{
    gc
Re: if expression
by fishbot_v2 (Chaplain) on Sep 16, 2005 at 14:14 UTC
    if $line not =~ (/INSERT INTO Message_pool)/{

    From perlsyn:

    if (EXPR) BLOCK elsif (EXPR) BLOCK ... else BLOCK

    The parens are optional on the statement modifier form of if, but not in the flow control case. Additionally:

    $line not =~ (/INSERT INTO Message_pool)/

    is not valid. You want either:

    ( not $line =~ m/INSERT INTO Message_pool/ ) # or more likely ( $line !~ m/INSERT INTO Message_pool/ )

    But perhaps the real question is why you test a condition to discard it, rather than combining this condition with the next... actually, I started to rewrite your conditional based on what I thought that you meant, but I can't figure it out. Is the second condition not unreachable? If the line is not an INSERT, do nothing, otherwise if it is a CREATE do something? It has to be both an INSERT and a CREATE.

    Other questions/comments:

    • What does #! perl -w scipt mean, and why are you using -w and use warnings?
    • You declare $line and $file in the broadest scope possible, only to mask them in inner scopes.

    updated

Re: if expression
by pg (Canon) on Sep 16, 2005 at 14:10 UTC

    Update: Forget about my comment. I put your code in one directory, but did a perl -c with a script that has the same name in a different directory. Need morning coffee. This has no problem with Perl 5.8.7, at least from syntax point of view.

    Update2: What you want is something like this:

    if ($line !~ /INSERT INTO Message_pool/) { #Do nothing } elsif ($line =~ /^.{0,50}CREATE TABLE\s([A-Z|a-z|_]{5,40})\s.{1,10 +0}/) {

    Other than the () issue. The way to express not match, is !~, not "not =~". By the way, if you are really fond of "not", to make the syntax right, you can say:

    if (not ($line =~ /INSERT INTO Message_pool/)) {