Problem with recursive file/directory copy

Discussion in 'Perl Misc' started by Anonymous user, Jan 12, 2006.

  1. Hello,

    i can't manage to make my program work, and i don't understand the
    error. I have the lines:

    $_ = $dst;
    my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    (m//) at copy.pl line 33.

    i would like to put in my variable the result of the extension test.
    Seems pretty simple...

    Here is my source code:

    ----------------------------------------------------------- 8<
    #!/usr/bin/perl -w

    use strict;
    use warnings;

    use File::Find;
    use File::Basename;
    use File::Copy;
    use File::path;

    # global variables
    use vars qw( $src_dir $dst_dir );

    my ($src_dir, $dst_dir) = @ARGV;

    sub copy_file {
    my $src = $File::Find::name;

    # remove the beginning of the path
    $_ = "$src";
    s!\Q$src_dir!!;

    # destination
    my $dst = "$dst_dir/$_";

    # is it a directory?
    if ( -d $src ) {
    mkpath([ $dst ]);
    } else {
    # if it's a perl file, say it!
    if (copy($src, $dst)) {
    $_ = $dst;
    my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    (m//) at copy.pl line 33.

    if ($test) {
    print("Perl file found: $dst\n");
    }
    } else {
    print STDERR "Can't copy file $src to $dst: $!\n";
    }
    }
    }

    # create the root destination directory
    mkpath([ $dst_dir ]);

    find(\&copy_file, $src_dir);

    ----------------------------------------------------------- 8<

    Thx for any help
     
    Anonymous user, Jan 12, 2006
    #1
    1. Advertising

  2. Anonymous user

    Paul Lalli Guest

    Anonymous user wrote:
    > Hello,
    >
    > i can't manage to make my program work, and i don't understand the
    > error. I have the lines:
    >
    > $_ = $dst;
    > my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    > (m//) at copy.pl line 33.
    >
    > i would like to put in my variable the result of the extension test.
    > Seems pretty simple...


    I don't at all understand what you're doing. You are assigning $_ to
    be the contents of the variable $dst. Then you are creating $test and
    immediately trying to do a pattern match on this new variable. The new
    variable is undefined when it's created. That's why you're getting the
    warning.

    If you are trying to assign $test to be the return value of a pattern
    match against $_, then you simply used the wrong operator:

    my $test = /\.pl\$/;

    Written more explicitly:
    my $test = ($_ =~ m/\.pl\$/);

    Without the seemingly pointless prior assignment to $_ :

    my $test = ($dst =~ m/\.pl\$/);

    Paul Lalli
     
    Paul Lalli, Jan 12, 2006
    #2
    1. Advertising

  3. Anonymous user wrote:
    > Hello,


    Hi Anonymous

    > i can't manage to make my program work, and i don't understand the
    > error. I have the lines:
    >
    > $_ = $dst;
    > my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    > (m//) at copy.pl line 33.


    What do you think this line is doing?
    You are just declaring $test and it doesn't contain any value yet and you
    are binding it to the pattern match. Well, of course you will get an
    "unitializad value" error.

    > i would like to put in my variable the result of the extension test.
    > Seems pretty simple...


    Did you mean
    my $test = m/\.pl\$/;


    If yes, then this whole section

    > $_ = $dst;
    > my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    > (m//) at copy.pl line 33.
    >
    > if ($test) {


    is easier written as
    if ($dst =~ m/\.pl\$/) {
    No need for all those one-time auxiliary variables.

    jue
     
    Jürgen Exner, Jan 12, 2006
    #3
  4. Anonymous user

    Paul Lalli Guest

    Anonymous user wrote:

    > Here is my source code:


    My previous post answered your question. Following is an attempt at a
    helpful critique of your code:


    > ----------------------------------------------------------- 8<
    > #!/usr/bin/perl -w
    >
    > use strict;
    > use warnings;


    Excellent that you are using strict and warnings. However, -w and use
    warnings; are (mostly) redundant. Get rid of the -w, and keep the use
    warnings.

    > use File::Find;
    > use File::Basename;
    > use File::Copy;
    > use File::path;
    >
    > # global variables
    > use vars qw( $src_dir $dst_dir );


    Why are you declaring global variables? Why are you using the Perl 4
    syntax to declare global variables?
    perldoc -f our

    > my ($src_dir, $dst_dir) = @ARGV;


    And now why are you over-riding those global variables by declaring
    lexical variables of the same name? Get rid of that entire use vars
    line above.

    > sub copy_file {
    > my $src = $File::Find::name;
    >
    > # remove the beginning of the path
    > $_ = "$src";


    perldoc -q "vars"

    > s!\Q$src_dir!!;


    You seem to have a fascination with assigning $_ before running a m//
    or s///. Are you unaware of the =~ operator?

    Furthermore, in a File::Find callback, $_ is an important variable (the
    name of the current file, without the path) that you have just
    obliterated.

    $src =~ s/\Q$src_dir//;

    So you first assigned $src to be the full path of the current file
    name. Then you overwrote $_ (which contained just the name of the
    file, without the path) with this full path of the file name. Then you
    performed a search-and-replace to remove the directory portion of the
    path, leaving you with.... the name of the file. Which is what was in
    $_ before you did anything to begin with!

    Eliminate every line of the subroutine up to this point.

    > # destination
    > my $dst = "$dst_dir/$_";
    >
    > # is it a directory?
    > if ( -d $src ) {


    $_ is the name of the file. And you are automatically chdir'd to
    $File::Find::dir. So a simple
    if ( -d ) {
    will work just fine.

    > mkpath([ $dst ]);
    > } else {
    > # if it's a perl file, say it!
    > if (copy($src, $dst)) {
    > $_ = $dst;
    > my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match
    > (m//) at copy.pl line 33.
    >
    > if ($test) {
    > print("Perl file found: $dst\n");
    > }


    This whole bit can be simplified to:
    if ( /\.pl\$ ) {
    print "Perl file found: $dst_dir/$_";
    }

    No need to assign to $_ or to create a separate variable for the result
    of the pattern match.

    > } else {
    > print STDERR "Can't copy file $src to $dst: $!\n";


    "print STDERR" is more conventionally spelled "warn"

    > }
    > }
    > }
    >
    > # create the root destination directory
    > mkpath([ $dst_dir ]);
    >
    > find(\&copy_file, $src_dir);


    Hope this helps,
    Paul Lalli
     
    Paul Lalli, Jan 12, 2006
    #4
  5. Anonymous user wrote:
    > Hello,
    >
    > i can't manage to make my program work, and i don't understand the
    > error. I have the lines:
    >
    > $_ = $dst;
    > my $test =~ m/\.pl\$/; # Use of uninitialized value in pattern match


    are you trying to match a literal '$', or are you trying to anchor the
    match to the end of the string? if the former, i think your regex is
    correct--if the latter, then don't escape the '$'.
     
    it_says_BALLS_on_your forehead, Jan 12, 2006
    #5
  6. Anonymous user

    Dr.Ruud Guest

    Paul Lalli schreef:

    > "print STDERR" is more conventionally spelled "warn"


    Yes, but isn't "warn" more than that too?
    I think there is (or can be) a difference in buffering.
    And "warn" obeys a $SIG{__WARN__} handler.

    --
    Affijn, Ruud

    "Gewoon is een tijger."
     
    Dr.Ruud, Jan 12, 2006
    #6
  7. Anonymous user

    Paul Lalli Guest

    Dr.Ruud wrote:
    > Paul Lalli schreef:
    >
    > > "print STDERR" is more conventionally spelled "warn"

    >
    > Yes, but isn't "warn" more than that too?
    > I think there is (or can be) a difference in buffering.


    Is there? I thought buffering was dependent on the stream being
    printed to, not to the name of the function that does the printing. I
    could be wrong. . .

    > And "warn" obeys a $SIG{__WARN__} handler.


    And warn will print the current file name and line number. IMO, all
    things that one should want to happen when sending a message to STDERR.
    ;-)

    Paul Lalli
     
    Paul Lalli, Jan 12, 2006
    #7
  8. Dr.Ruud <> wrote:
    > Paul Lalli schreef:
    >
    >> "print STDERR" is more conventionally spelled "warn"

    >
    > Yes, but isn't "warn" more than that too?



    Yes, if the last argument does not end with a newline.


    --
    Tad McClellan SGML consulting
    Perl programming
    Fort Worth, Texas
     
    Tad McClellan, Jan 12, 2006
    #8
  9. Thx to you all !!

    I took me some time to answer:

    1/ \$ -> $ => didn't work

    2/ use warning superseeds -w => OK

    3/ global variables: needed in find callback ("copy_file")

    our ($src_dir, $dst_dir);

    ($src_dir, $dst_dir) = @ARGV;

    4/ unaware of =~ => no but not able to easily use it: yes (i'm a newb)

    5/ you misunderstood something:
    in copy_file:
    * $File::Find::name is "$src_dir/path/fname"
    * $_ is "fname"
    * i want/need "path/fname"

    6/ ok for ( -d )

    7/ ok for warn()

    So here is the new version:

    ================================= >8
    #!/usr/bin/perl

    use strict;
    use warnings;

    use File::Find;
    use File::Copy;
    use File::path;

    if ($#ARGV != 1) {
    exit(1);
    }

    # globals for callback
    our ($src_dir, $dst_dir);

    ($src_dir, $dst_dir) = @ARGV;

    if (! -e $src_dir ) {
    exit(1);
    }

    # - $File::Find::name contains the absolute filename
    # - $File::Find::dir contains the absolute path to the parent directory
    # the parent directory is made current with chdir just before calling
    # - $_ contains the filename (without any path)
    #
    sub copy_file {
    my $src = $File::Find::name;

    # relative path from $src_dir
    my $relative_pathname = $src;
    $relative_pathname =~ s!\Q$src_dir!!;

    # destination
    my $dst = "$dst_dir/$relative_pathname";

    # is it a directory? ($_)
    if ( -d ) {
    mkpath([ $dst ]);
    } else {
    if (copy($src, $dst)) {
    # if it's a perl file, say it!
    if (/\.pl$/) {
    print("Perl file found: $dst\n");
    }
    } else {
    warn("Can't copy file $src to $dst: $!\n");
    }
    }
    }

    # create the root destination directory
    mkpath([ $dst_dir ]);

    find(\&copy_file, $src_dir);
    ======================================== >8

    Thx again for your help
     
    Anonymous user, Jan 13, 2006
    #9
  10. Anonymous user

    Paul Lalli Guest

    Anonymous user wrote:
    > Thx to you all !!


    You can best show your apprecation by quoting some context when you
    reply, so we have a clue what the heck you're talking about.

    Please read the Posting Guidelines for this group

    > I took me some time to answer:
    >
    > 1/ \$ -> $ => didn't work


    That's a terrible error description. In what way did the change "not
    work"?

    > 3/ global variables: needed in find callback ("copy_file")


    No, they're not. You can use lexicals just fine. Lexical variables
    are available to all code written within the scope of their
    declaration, including any subroutines defined there.

    > our ($src_dir, $dst_dir);
    >
    > ($src_dir, $dst_dir) = @ARGV;


    change those two lines to:
    my ($src_dir, $dst_dir) = @ARGV;

    >
    > 4/ unaware of =~ => no but not able to easily use it: yes (i'm a newb)


    =~ is the "binding" variable. It determines what string you are
    performing your m// or s/// on.
    $string =~ /foobar/
    searches $string for the pattern 'foobar'. If you do not provide the
    =~ operator, Perl assumes you want to perform the operation on $_.
    That is,
    /foobar/
    is equivalent to:
    $_ =~ /foobar/


    > So here is the new version:
    >
    > ================================= >8
    > #!/usr/bin/perl
    >
    > use strict;
    > use warnings;
    >
    > use File::Find;
    > use File::Copy;
    > use File::path;
    >
    > if ($#ARGV != 1) {
    > exit(1);
    > }
    >
    > # globals for callback
    > our ($src_dir, $dst_dir);
    >
    > ($src_dir, $dst_dir) = @ARGV;
    >
    > if (! -e $src_dir ) {
    > exit(1);
    > }
    >
    > # - $File::Find::name contains the absolute filename
    > # - $File::Find::dir contains the absolute path to the parent directory
    > # the parent directory is made current with chdir just before calling
    > # - $_ contains the filename (without any path)
    > #
    > sub copy_file {
    > my $src = $File::Find::name;
    >
    > # relative path from $src_dir
    > my $relative_pathname = $src;
    > $relative_pathname =~ s!\Q$src_dir!!;
    >
    > # destination
    > my $dst = "$dst_dir/$relative_pathname";
    >
    > # is it a directory? ($_)
    > if ( -d ) {
    > mkpath([ $dst ]);
    > } else {
    > if (copy($src, $dst)) {
    > # if it's a perl file, say it!
    > if (/\.pl$/) {
    > print("Perl file found: $dst\n");
    > }
    > } else {
    > warn("Can't copy file $src to $dst: $!\n");
    > }
    > }
    > }
    >
    > # create the root destination directory
    > mkpath([ $dst_dir ]);
    >
    > find(\&copy_file, $src_dir);
    > ======================================== >8



    I have a dumb question at this point. I never really bothered to look
    at the "intent" of this script before. But now that I have, is there
    any particular reason you're not just doing:

    cp -R source_dir dest_dir

    rather than writing an entire Perl program?

    Paul Lalli
     
    Paul Lalli, Jan 13, 2006
    #10
  11. Paul Lalli a écrit :

    > > 1/ \$ -> $ => didn't work

    >
    > That's a terrible error description. In what way did the change "not
    > work"?


    "whith \$, the extension test wasn't working, but with $ it works now"


    > > 3/ global variables: needed in find callback ("copy_file")

    >
    > No, they're not. You can use lexicals just fine. Lexical variables
    > are available to all code written within the scope of their
    > declaration, including any subroutines defined there.
    >
    > > our ($src_dir, $dst_dir);
    > >
    > > ($src_dir, $dst_dir) = @ARGV;

    >
    > change those two lines to:
    > my ($src_dir, $dst_dir) = @ARGV;


    ok i'll try this


    > > 4/ unaware of =~ => no but not able to easily use it: yes (i'm a newb)

    >
    > =~ is the "binding" variable. It determines what string you are
    > performing your m// or s/// on.
    > $string =~ /foobar/
    > searches $string for the pattern 'foobar'. If you do not provide the
    > =~ operator, Perl assumes you want to perform the operation on $_.
    > That is,
    > /foobar/
    > is equivalent to:
    > $_ =~ /foobar/


    ok, thx


    > I have a dumb question at this point. I never really bothered to look
    > at the "intent" of this script before. But now that I have, is there
    > any particular reason you're not just doing:
    >
    > cp -R source_dir dest_dir
    >
    > rather than writing an entire Perl program?


    because i'm working on Windows (very soon it will change), and i don't
    want to depend on Unix/GNU/Posix tools.
     
    Anonymous user, Jan 16, 2006
    #11
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Alex
    Replies:
    2
    Views:
    1,242
  2. Replies:
    26
    Views:
    2,129
    Roland Pibinger
    Sep 1, 2006
  3. n00m
    Replies:
    12
    Views:
    1,119
  4. Jerry Krinock
    Replies:
    1
    Views:
    234
  5. Gordon
    Replies:
    1
    Views:
    222
    nolo contendere
    Apr 14, 2008
Loading...

Share This Page