Problem with recursive file/directory copy

A

Anonymous user

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
 
P

Paul Lalli

Anonymous said:
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
 
J

Jürgen Exner

Anonymous said:

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
 
P

Paul Lalli

Anonymous said:
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
 
I

it_says_BALLS_on_your forehead

Anonymous said:
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 '$'.
 
D

Dr.Ruud

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.
 
P

Paul Lalli

Dr.Ruud said:
Paul Lalli schreef:


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
 
A

Anonymous user

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
 
P

Paul Lalli

Anonymous said:
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
 
A

Anonymous user

Paul Lalli a écrit :
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"

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.


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

ok i'll try this

=~ 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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top