in reply to Re^2: getting a few simple scripts to work on windows
in thread getting a few simple scripts to work on windows

Okay, since you're asking questions, I'm trying to re-familiarize myself with it; that was all Oct of last year, and I hadn't thought about it since. ...

The isjunc() and is_junction() are effectively two different ways for testing for whether a file is a junction. The isjunc() is a much simpler way of doing it -- it doesn't require spawning to the shell's dir /AL /B, and is (presumably) much more efficient. If you comment out just the f32attr call in my example script, you can see that is_junction (which results in a capital J in the printout) and isjunc (which results in a lower-case j) both trigger on the same files. Basically, with that, I was trying to prove to myself that isjunc really found all the junctions. (I knew that windows dir should, so was comparing it as "golden" to my "experimental" isjunc.) Specific answers to your questions in the readmore...

Q1 Where does the code for the is_junc and f32attr subs go?

In the script which is using File::Find, I just add the lines

#### https://metacpan.org/pod/Win32API::File use Win32API::File qw'GetFileAttributes :FILE_ATTRIBUTE_'; sub isjunc { return GetFileAttributes($_[0]) & FILE_ATTRIBUTE_REPARSE_POINT }

The f32attr was just a debug function, and is not needed. Similarly, I don't use the older is_junction() or its helper list_junctions() which shelled out, either, since they were just verifying that I could get accurate junction-identification. All that is needed for a junction test is the injunc listed above.

Q3) What is the simplest way to test whether a file is a junction, and if it is, simply ignore it whilst recursing through the rest of them?

In my wanted() function (which is what gets passed to the File::Find::find() call), I have a section which says

if ( -d ) { # junctions show up as TRUE for `-d` if( isjunc($File::Find::name) ) { # if it's a junction, don't bother creating checking desti +nation directory existence, # and don't try to recurse into it $File::Find::prune = 1; printf ("[%s] %-12s'%s'\n", scalar(localtime), "JUNCTION:" +, $File::Find::name) if ($VERBOSE); return; } ... # go do the stuff I want to do if it's a real directory, a +nd not a junction }

The $File::Find::prune is documented in File::Find as the way to prevent recursing into that directory (or junction)

Q2 Am I correct to surmise that File::Find is essentially broken for windows? (Others may say windows is broken without the point being different.)

I disagree with that assessment. It doesn't have an ideal handling of junctions -- it just prints a warning, but otherwise cannot do anything with the junction -- but other than that, it works fine in windows... and even with junctions, it just gives a warning, so it continues to effectively work. Thus, arguably buggy, but not effectively broken. (Junctions, IMO, are B.A.D., but that's windows' fault, not File::Find's fault. And while it might have been nice if File::Find could recurse into the junction, the warning-only result is at least a "do-no-harm" response to the strange junction.

Replies are listed 'Best First'.
Re^4: getting a few simple scripts to work on windows
by Aldebaran (Curate) on Aug 16, 2019 at 06:09 UTC

    Your recollections and posted code check out. Bravo! I consider this a strong result for untangling a windows 10 machine:

    #!/usr/bin/perl use warnings; use 5.016; ## File::Find for windows, handling junctions ## https://perlmonks.org/?node_id=11104464 use File::Find; use Cwd; ## https://metacpan.org/pod/Win32API::File use Win32API::File qw'GetFileAttributes :FILE_ATTRIBUTE_'; use File::Basename; my $current = cwd; my $VERBOSE = 1; find( \&pm_beneath, $current, ); say "--------------"; find( \&pm_beneath, "C:/Strawberry", ); sub isjunc { return GetFileAttributes( $_[0] ) & FILE_ATTRIBUTE_REPARSE_POINT; } sub pm_beneath { if (-d) { # junctions show up as TRUE for `-d` if ( isjunc($File::Find::name) ) { # if junction, don't bother creating checking destination direct +ory existence, # and don't try to recurse into it $File::Find::prune = 1; printf( "[%s] %-12s'%s'\n", scalar(localtime), "JUNCTION:", $File::Find::name ) if ($VERBOSE); return; } } my $basename = basename($File::Find::name); return unless $basename =~ /\.pm$/; print "$File::Find::name\n"; my $access_age = -A $basename; print " $basename\n"; printf "access age in days: %.2f\n", $access_age; } __END__
    (File::Find) just prints a warning, but otherwise cannot do anything with the junction -- but other than that, it works fine in windows... and even with junctions, it just gives a warning, so it continues to effectively work.

    I understand and agree now. The other script I presented in the original post is still ailing:

    Thanks for comments,

      Can I make a template from a garden variety lexical variable?

      When I'm silent on threads where I've asked questions, it is usually time where I hit the reading that responders post and try out all the elbow grease I can muster, as I make keystrokes writing scripts that are imperfect. Occasionally, I get a couple good ones to post, and I think this falls into that category.

      Text::Template is too big a hammer for this nail. Native perl is enough.

      my $tag_pair = '<x></x>'; new_string =~ s/x/${letter}/g;

      The system call at the end of this is for windows. I intend to ask the perl hive mind how to make a grown-ups' switch control to probe for what OS the script is being run on, but not here, as the premise that I was on a windows machine was in the original post. Furthermore, I'm 5 deep in terms of the response levels, and I don't want to start anything new. It's impossible to show the output without running a script on it to substitute out special characters, the ones you always need in better writeups. We want to end up with a real file, so we can make that the basis of showing code and discussing it in terms of other sources on pm and the internet. Better writeups reference others, IMO. I guess what I can show for output is the ultimate file created by Path::Tiny:

      created file C:/Users/tblaz/Documents/evelyn/perlmonks/writeups/21-08-2019-10-33-27.monk.txt

      Source:

      #!/usr/bin/perl -w use 5.011; use Path::Tiny; use POSIX qw(strftime); # initialization that must precede main data structure # User: enter a subdirectory you would like to create # enter a subdirectory of this^^^ for output my $ts = "perlmonks"; my $output = "writeups"; ## turning things to Path::Tiny my $abs = path(__FILE__)->absolute; my $path1 = Path::Tiny->cwd; my $path2 = path( $path1, $ts ); say "abs is $abs"; say "path1 is $path1"; say "path2 is $path2"; print "This script will build the above path2. Proceed? (y|n)"; my $prompt = <STDIN>; chomp $prompt; die unless ( $prompt eq "y" ); ## special do-hickeys I want at the end my @list = ( '[id://3989]', '[|]', '[|]', '&lt;', '&gt;', '&#91;', '&# +93;' ); @list = map { "$_\n" } @list; say "list is @list"; my $return1 = write_monk_tags(); say "return1 is $return1"; my $munge = strftime( "%d-%m-%Y-%H-%M-%S", localtime ); $munge .= ".monk.txt"; # use Path::Tiny to create and write to a text in relevant directory my $save_file = path( $path2, $output, $munge )->touchpath; my $return2 = $save_file->spew($return1); say "return2 is $return2"; my $return3 = $save_file->append(@list); say "return3 is $return3"; say "created file $save_file"; system( 1, 'C:\Program Files (x86)\Notepad++\notepad++.exe', $save_fil +e ); sub write_monk_tags { use warnings; use 5.011; my $tag_pair = '<x></x>'; my $return = ''; # User: change these quoted values for different order or tags my @buchstaben = qw/i p c pre readmore b/; for my $letter (@buchstaben) { print "How many $letter tag pairs would you like?: "; my $prompt = <STDIN>; chomp $prompt; while ( $prompt gt 0 ) { my $new_string = $tag_pair; $new_string =~ s/x/${letter}/g; say "new string is $new_string"; $return = $return . $new_string . "\n"; --$prompt; } } return $return; } __END__

      Of course, I'll consider any criticism of this. Thx pryrt for your comments to get me moving on this new machine.