http://www.perlmonks.org?node_id=595120

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

I have a Perl script that is supposed to monitor a pre-defined list of UNC paths and make sure there are no files waiting to be processed in those directories. The first thing the script does is read in a list of UNC's (in the sample below - from DATA). It then puts these UNC's into a Hash of Arrays (no problem so far - everything work nicely). Next, I want to loop through each of those array's and scan the directory. When I pull the UNC's back out of the array, I run into problems, mainly where the "\\" at the start is escaped down to "\" (I think this is because of the single quote that I am inserting to account for the spaces in the file names). I don't have any control over the names in the UNCs - but if I need to tweak the data a bit to account for spaces and quotes in the paths, I can do that.

My code so far (streamlined to display problem):

use File::Spec; use Data::Dumper; use strict; use warnings; my @dir_list; my $rec; my %HoA; foreach my $dir (<DATA>) { chomp $dir; my $first=substr($dir,0,1); if ($first =~ /#/) { next; } elsif ( $first =~ /=/ ) { if ($dir=~/(^\={2})(\w+)(\={2})/) { $rec=$2; } @dir_list=(); #reset the list of directories to empty } else { $dir=File::Spec->catdir("\'$dir\'"); push @dir_list, $dir; #add directory name to array $HoA{$rec}=[ @dir_list ]; #add array of directories to + HoA }#close if }#close foreach ###################################################################### +####################################### print Data::Dumper->Dump([\%HoA], ['*HoA']); for my $server ( keys %HoA ) { chomp $server; for my $dir ( <@{ $HoA{$server} }> ) { chomp $dir; print "$server: $dir\n"; } } __DATA__ ########## Server 1 UNC's ######################## ==SERVER1== \\server1\share1\tld0\sub-dir1\sub-dir2 \\server1\share1\tld0\sub-dir2\sub-dir2 \\server1\share2\tld1\dir1\subdir2\subdir3 \\server1\share2\tld1\dir1\subdir2\subdir4 ########## Other Server UNC's ######################## ==REMOTESERVER1== \\remoteserver1\share1\tld1\dir1\subdir2\subdir4 ==REMOTESERVER2== \\remoteserver2\share1\tld1\dir1\sub dir 3\sub dir 5\report's \\remoteserver2\share1\tld1\dir2\sub dir 4\sub dir 6\reports ###### No Entries Below This Line ######
Here is my output:

%HoA = ( 'REMOTESERVER1' => [ '\'\\remoteserver1\\share1\\tld1\\dir1\\ +subdir2\\subdir4\'' ], 'SERVER1' => [ '\'\\server1\\share1\\tld0\\sub-dir1\\sub-dir2 +\'', '\'\\server1\\share1\\tld0\\sub-dir2\\sub-dir2 +\'', '\'\\server1\\share2\\tld1\\dir1\\subdir2\\sub +dir3\'', '\'\\server1\\share2\\tld1\\dir1\\subdir2\\sub +dir4\'' ], 'REMOTESERVER2' => [ '\'\\remoteserver2\\share1\\tld1\\dir1\\ +sub dir 3\\sub dir 5\\report\'s\'', '\'\\remoteserver2\\share1\\tld1\\dir2\\ +sub dir 4\\sub dir 6\\reports\'' ] ); REMOTESERVER1: '\remoteserver1\share1\tld1\dir1\subdir2\subdir4' SERVER1: \server1\share1\tld0\sub-dir1\sub-dir2 SERVER1: \server1\share1\tld0\sub-dir2\sub-dir2 SERVER1: \server1\share2\tld1\dir1\subdir2\subdir3 SERVER1: \server1\share2\tld1\dir1\subdir2\subdir4 REMOTESERVER2: '\remoteserver2\share1\tld1\dir1\sub dir 3\sub dir 5\re +port's' '\remoteserver2\share1\tld1\dir2\sub dir 4\sub dir 6\reports'

What I expect to see in the output would be something along the lines of:

REMOTESERVER1: \\remoteserver1\share1\tld1\dir1\subdir2\subdir4 SERVER1: \\server1\share1\tld0\sub-dir1\sub-dir2 SERVER1: \\server1\share1\tld0\sub-dir2\sub-dir2 SERVER1: \\server1\share2\tld1\dir1\subdir2\subdir3 SERVER1: \\server1\share2\tld1\dir1\subdir2\subdir4 REMOTESERVER2: \\remoteserver2\share1\tld1\dir1\sub dir 3\sub dir 5\re +port's REMOTESERVER2: \\remoteserver2\share1\tld1\dir2\sub dir 4\sub dir 6\re +ports

Any thoughts on working through this issue would be appreciated. I have tried wrapping single and double quotes around the UNC's, flipping the backslashes to forward slashes and nothing really seems to work (a few things make it worse such as removing the directory seperators completely).

update: figured out the problem as thundergnat points out below.

Replies are listed 'Best First'.
Re: Windows Paths
by thundergnat (Deacon) on Jan 17, 2007 at 20:44 UTC

    Not sure what you are trying to accomplish with the line

    $dir=File::Spec->catdir("\'$dir\'");

    I suspect it was an attempt at a work around, but it's not helping you. Get rid of it.

    I suspect your main problem is in the line

    for my $dir ( <@{ $HoA{$server} }> ) {

    The angle brackets will perform one level of double quote interpolation on anything that is not a file handle. (It thinks it's a glob.)

    Lessee...

    From perlop - I/O operators

    If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context. ... One level of double-quote interpretation is done first,...
      Thanks. I think I'd just figured that out by using a different method to print the UNC's. Nice to have a good explanation of that.
Re: Windows Paths
by johngg (Canon) on Jan 17, 2007 at 21:40 UTC
    thundergnat has pointed out the problem with the accidental globbing. I think you can make the code simpler and easier to read if you dispense with the intermediate @dir_list variable because you can autovivify each array directly. I have used while (<DATA>) {...} to read into $_ so that matches and the chomp are less cluttered. I think I'm right in saying (and I'm sure I'll be corrected if I'm wrong) that the foreach will also read the entire file in order to build a list that it can iterate over; possibly not what you want.

    I have left the chomp until after rejecting the comment lines; no point in doing work on something we're going to throw away. You don't need to escape the equals in the match and I'm not sure why you were capturing the equals signs either side of the server name; perhaps there was a reason that has been lost as you reduced the code for your post.

    use strict; use warnings; use Data::Dumper; my $rec = q{}; my %HoA = (); while (<DATA>) { next if m{^#}; chomp; if (m{^==(\w+)==}) { $rec = $1; } else { push @{$HoA{$rec}}, $_; } } print Data::Dumper->Dump([\%HoA], ['*HoA']); print qq{\n}; foreach my $server (keys %HoA) { foreach my $unc (@{$HoA{$server}}) { print qq{$server: $unc\n}; } } __DATA__ ########## Server 1 UNC's ######################## ==SERVER1== \\server1\share1\tld0\sub-dir1\sub-dir2 \\server1\share1\tld0\sub-dir2\sub-dir2 \\server1\share2\tld1\dir1\subdir2\subdir3 \\server1\share2\tld1\dir1\subdir2\subdir4 ########## Other Server UNC's ######################## ==REMOTESERVER1== \\remoteserver1\share1\tld1\dir1\subdir2\subdir4 ==REMOTESERVER2== \\remoteserver2\share1\tld1\dir1\sub dir 3\sub dir 5\report's \\remoteserver2\share1\tld1\dir2\sub dir 4\sub dir 6\reports ###### No Entries Below This Line ######

    Here's the output. Note that Data::Dumper does it's own escaping when serializing the data.

    %HoA = ( 'REMOTESERVER1' => [ '\\\\remoteserver1\\share1\\tld1\\dir1\\ +subdir2\\subdir4' ], 'SERVER1' => [ '\\\\server1\\share1\\tld0\\sub-dir1\\sub-dir2 +', '\\\\server1\\share1\\tld0\\sub-dir2\\sub-dir2 +', '\\\\server1\\share2\\tld1\\dir1\\subdir2\\sub +dir3', '\\\\server1\\share2\\tld1\\dir1\\subdir2\\sub +dir4' ], 'REMOTESERVER2' => [ '\\\\remoteserver2\\share1\\tld1\\dir1\\ +sub dir 3\\sub dir 5\\report\'s', '\\\\remoteserver2\\share1\\tld1\\dir2\\ +sub dir 4\\sub dir 6\\reports' ] ); REMOTESERVER1: \\remoteserver1\share1\tld1\dir1\subdir2\subdir4 SERVER1: \\server1\share1\tld0\sub-dir1\sub-dir2 SERVER1: \\server1\share1\tld0\sub-dir2\sub-dir2 SERVER1: \\server1\share2\tld1\dir1\subdir2\subdir3 SERVER1: \\server1\share2\tld1\dir1\subdir2\subdir4 REMOTESERVER2: \\remoteserver2\share1\tld1\dir1\sub dir 3\sub dir 5\re +port's REMOTESERVER2: \\remoteserver2\share1\tld1\dir2\sub dir 4\sub dir 6\re +ports

    I hope this is of use.

    Cheers,

    JohnGG