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

Thanks for your pithy reply, pryrt. I wish my reply could be so concise, but I'm still grabbing at straws. I try to save the vertical space for responders so will put the rest of what I have that pertains between readmore tags:

...the magic that windows uses for the "My Documents" and similar folders -- junctions

With a couple lines of your script commented out, I'm getting good preliminary results:

C:/Users/tblaz/1.modules.pl C:/Users/tblaz/1.txt C:/Users/tblaz/2.txt $VAR1 = { 'Start Menu' => 1, 'Application Data' => 1, 'My Documents' => 1, 'Cookies' => 1, 'Local Settings' => 1, 'Templates' => 1, 'Recent' => 1, 'NetHood' => 1, 'PrintHood' => 1, 'SendTo' => 1 }; d C:/Users/tblaz/3D Objects C:/Users/tblaz/5.txt d C:/Users/tblaz/AppData Jd C:/Users/tblaz/Application Data d C:/Users/tblaz/Contacts Jd C:/Users/tblaz/Cookies d C:/Users/tblaz/Desktop d C:/Users/tblaz/Documents ... Jd C:/Users/tblaz/Start Menu Jd C:/Users/tblaz/Templates d C:/Users/tblaz/Videos $VAR1 = {}; d C:/Strawberry/c d C:/Strawberry/cpan C:/Strawberry/DISTRIBUTIONS.txt d C:/Strawberry/licenses d C:/Strawberry/perl C:/Strawberry/README.txt C:/Strawberry/relocation.txt d C:/Strawberry/win32

#!/usr/bin/perl -w use 5.011; ## inspired by https://perlmonks.org/?node_id=1223819 ## my old link to a junction post => https://perlmonks.org/?node_ +id=1178059 ## found https://github.com/dagolden/Path-Tiny/issues/160 (which is wh +ere these functions came from) ## added the -d and -l tests use Path::Tiny; use Win32API::File qw'GetFileAttributes :FILE_ATTRIBUTE_'; for my $p (glob('C:/Users/tblaz/*'), glob('C:/Strawberry/*') ) { print is_junction($p) ? "J" : " "; print -d($p) ? "d" : " "; print -l($p) ? "l" : " "; #print f32attr($p); #print isjunc($p) ? " j " : " "; print " $p\n" } sub is_junction { my ($dir) = @_; state $last_parent; state $junction_by; my $path = path($dir); if (! $path->is_dir || $path->is_rootdir) { return 0; } if (! defined $last_parent || $path->parent ne $last_parent) { $junction_by = { map { $_ => 1 } list_junctions($path->parent) + }; use Data::Dumper; print Dumper $junction_by; no Data::Dump +er; $last_parent = $path->parent; } return exists $junction_by->{$path->basename}; } sub list_junctions { my ($dir) = @_; my $path = path($dir); if (! $path->is_dir) { return (); } my $cmd = sprintf 'dir /AL /B "%s" 2>&1', $path->canonpath; my @lines = `$cmd`; chomp @lines; if ($? >> 8) { if ($lines[0] eq 'File Not Found') { return (); } else { die "Failed to execute: $cmd"; } } return @lines; } __END__

You post code for the functions I commented out. Where is that supposed to go? I have:

cpan> install Win32API::File Win32API::File is up to date (0.1203). cpan>

My module-finding script works despite the complaints to STDOUT, showing a few different places where they end up. I could hardly believe how many matches I got from File.pm:

C:/Strawberry/cpan/build/LWP-Online-1.08-0/inc/Module/Install/Makefile +.pm Makefile.pm access age in days: 8.24 ... C:/Strawberry/perl/lib/Win32API/File.pm File.pm access age in days: 8.25 ... C:/Strawberry/perl/vendor/lib/Win32/File.pm File.pm access age in days: 8.25

Now for the questions.

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

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

With this coming from the terminal:

C:\Users\tblaz>dir /AL Volume in drive C is Windows Volume Serial Number is ECCC-9917 Directory of C:\Users\tblaz 08/06/2019 02:22 AM <JUNCTION> Application Data [C:\Users\tbla +z\AppData\Roaming] 08/06/2019 02:22 AM <JUNCTION> Cookies [C:\Users\tblaz\AppData +\Local\Microsoft\Windows\INetCookies] 08/06/2019 02:22 AM <JUNCTION> Local Settings [C:\Users\tblaz\ +AppData\Local] 08/06/2019 02:22 AM <JUNCTION> My Documents [C:\Users\tblaz\Do +cuments] ... 08/06/2019 02:22 AM <JUNCTION> Start Menu [C:\Users\tblaz\AppD +ata\Roaming\Microsoft\Windows\Start Menu] 08/06/2019 02:22 AM <JUNCTION> Templates [C:\Users\tblaz\AppDa +ta\Roaming\Microsoft\Windows\Templates] 0 File(s) 0 bytes 10 Dir(s) 459,449,827,328 bytes free C:\Users\tblaz>dir /AL /B Application Data Cookies Local Settings My Documents NetHood PrintHood Recent SendTo Start Menu Templates C:\Users\tblaz>dir /AL /B My* My Documents C:\Users\tblaz>

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?

Thanks for your comment

Replies are listed 'Best First'.
Re^3: getting a few simple scripts to work on windows
by pryrt (Abbot) on Aug 14, 2019 at 16:21 UTC

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

      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.