use strict; use warnings; use File::Spec; my @arr = ( { 'path' => '/c/', dir => '/' }, { 'path' => '/c/a1.mp3', 'file' => 1, 'dir' => '/c' }, { 'path' => '/c/a2.mp3', 'file' => 1, 'dir' => '/c' }, { 'path' => '/c/bb/aa1.mp3', 'file' => 1, 'dir' => '/c/bb' }, { 'path' => '/c/bb/aa2.mp3', 'file' => 1, 'dir' => '/c/bb' }, { 'path' => '/c/bb/', 'dir' => '/c' }, { 'path' => '/c/cc/', 'dir' => '/c' }, { 'path' => '/c/aa/', 'dir' => '/c' } ); my $TAB_WIDTH=3; my $FORMAT="%-30s%s\n"; #----------------------------------------- # The sorting function #----------------------------------------- sub parsePathPortably { my $hPathInfo = shift; my $sPath = $hPathInfo->{path}; my $bFile = $hPathInfo->{file}; my ($sVol, $sDir, $sLocal) = File::Spec->splitpath($sPath, !$bFile); # File::Spec->splitdir assumes $dir is a relative path # without this line the first element of @aDirs on *nix is # an empty directory (because nothing is before root) my $sRelPath = File::Spec->abs2rel($sDir, File::Spec->rootdir()); my @aDirs =File::Spec->splitdir($sRelPath); # get indentation level and local file name my $iDepth = scalar(@aDirs); unless ($bFile) { $iDepth--; $sLocal = pop @aDirs; } # reconstruct the path, but before each directory name # segment insert 0x000 and before each file name segment # insert 0x001. It is important to insert something # before *all* directory name elements to insure # that the "b" segment of /a/b/c and /a/b sort properly # # we also insert before file names on the off,off chance # that you run into a file system where there is a file # name that begins with 0x000. DOS and most *nix systems # prohibit 0x000 in file names, but not all # - see [Wikipedia://Filename] for a list of OS's where # 0x000 does not appear to be in the list of reserved or # prohibited characters, among them: OS2, various Mac OS's my $k = File::Spec->catpath ($sVol, File::Spec->catdir(map{ord(0).$_} @aDirs) , ord($bFile ? 1 : 0) . $sLocal); return $k => [ $iDepth, $sPath, $sLocal ]; } #-------------------------------------------------- # Demonstration of how to use the sorting function #-------------------------------------------------- my $hPaths = { map { parsePathPortably($_) } @arr }; foreach my $k (sort keys %$hPaths) { my ($iDepth, $sPath, $sLocal) = @{$hPaths->{$k}}; my $sIndent = ' ' x ($TAB_WIDTH*$iDepth); printf($FORMAT, "$sIndent$sLocal", $sPath); } #### c /c/ aa /c/aa/ bb /c/bb/ aa1.mp3 /c/bb/aa1.mp3 aa2.mp3 /c/bb/aa2.mp3 cc /c/cc/ a1.mp3 /c/a1.mp3 a2.mp3 /c/a2.mp3