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

Hello Monks,

I read an article this week about how perl was extinct, and thought, "that's an odd story to come up in my newsfeed while I'm installing Strawberry Perl." It got me to thinking, "how do they corral and interview every perl hacker out there?" I hope that the simple software tools I develop will be portable, where possible. That's why I spend time developing on MS, and I find that it is *unbearable* without perl.

I have a few scripts that are essential to me tapping out my confines and being able to converse about it. The first is this. I include the msdos equivalent of cat, which is type:

C:\Users\tblaz>type 1.modules.pl #!/usr/bin/perl use warnings; use 5.016; use File::Find; use Cwd; =pod =head1 DESCRIPTION This is a pretty good first attempt at figuring out where your modules + are. It is meant to follow the development of _Intermediate Perl_, a +nd I will adhere to the idioms. =cut my $current = cwd; find( \&pm_beneath, $current, ); say "--------------"; find( \&pm_beneath, "C:", ); sub pm_beneath { use File::Basename; 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__ C:\Users\tblaz>

This gives me the output I'm looking for, but I generate a litany of errors on STDOUT whilst doing so. They're unwanted and clogging up STDOUT:

Microsoft Windows [Version 10.0.17763.615] (c) 2018 Microsoft Corporation. All rights reserved. C:\Users\tblaz>perl 1.modules.pl >5.txt ... at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/Documents/My Pictures): Invalid argument at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/Documents/My Videos): Invalid argument at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/Local Settings): Invalid argument at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/My Documents): Invalid argument at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/NetHood): Invalid argument at 1.modules.pl line 18. Can't opendir(C:/Users/tblaz/PrintHood): Invalid argument ... at 1.modules.pl line 20. Can't opendir(C:Templates): Invalid argument at 1.modules.pl line 20. C:\Users\tblaz>

How do rewrite this script to handle this error? edit These were only warnings, not errors. File::Find did what it should have, considering what junctions are. /edit

This has been my basic template for perlmonks writeups. It works except for the system call at the end:

#!/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 = "template_stuff"; my $output = "translations"; ## 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" ); my $template_file = "1.tags.tmpl"; my $abs_to_template = path( $path2, $template_file )->touchpath; # script params my %vars = ( monk_tags => path( $path2, $template_file ), translations => path( $path2, $output ), book => 'monastery tags ', ); my $rvars = \%vars; my $return1 = write_monk_tags($rvars); 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( $vars{$output}, $munge )->touchpath; my $return2 = $save_file->spew_utf8($return1); say "return2 is $return2"; say "created file $save_file"; # system("Run 'C:\Program Files (x86)\Notepad++\notepad++.exe' $save_f +ile"); sub write_monk_tags { use warnings; use 5.011; use Text::Template; my $rvars = shift; my %vars = %$rvars; my $body = $vars{"monk_tags"}; my $template = Text::Template->new( ENCODING => 'utf8', SOURCE => "$body", ) or die "Couldn't construct template: $!"; my $return = "$vars{\"book\"}\n"; # User: change these quoted values for different order or tags my @buchstaben = qw/i p c pre readmore b/; for my $i (@buchstaben) { $vars{"symbol"} = $i; print "How many $i tag pairs would you like?: "; my $prompt = <STDIN>; chomp $prompt; if ( $prompt lt 1 ) { $prompt = 0; } while ( $prompt gt 0 ) { my $result = $template->fill_in( HASH => \%vars ); $return = $return . $result; --$prompt; } } return $return; } __END__

What I have had is

gedit $save_file &

which works fine where gedit existed.

Notepad++'s executable is here:

'C:\Program Files (x86)\Notepad++\notepad++.exe'

I threw the Run in there as a desperate attempt. How do I start up an executable and pass it the value of a lexical variable?

Thank you for your reply,

Replies are listed 'Best First'.
Re: getting a few simple scripts to work on windows
by pryrt (Abbot) on Aug 09, 2019 at 20:38 UTC

    the first one is because of the magic that windows uses for the "My Documents" and similar folders -- junctions. I have a couple links that I store in my scratchpad about junctions: Re^3: how to handle the warnings in a find statement and <JUNCTION>; how to recognize ... And the code that is in the private repo link (sorry, that's there for my benefit, not anyone else's) can be found in the spoiler. Please note: I make no guarantees about this code.

    #!/usr/bin/env perl ## 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 5.010; # required to enable "state" for my $p (glob('C:/Users/peter.jones/*'), glob('c:/users/Public/*') ) + { 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; } #### https://metacpan.org/pod/Win32API::File use Win32API::File qw'GetFileAttributes :FILE_ATTRIBUTE_'; sub isjunc { return GetFileAttributes($_[0]) & FILE_ATTRIBUTE_REPARSE_POINT #return (GetFileAttributes($_[0]) & FILE_ATTRIBUTE_REPARSE_POINT = += FILE_ATTRIBUTE_REPARSE_POINT) ? 1 : 0; } sub f32attr { my $arg = shift; my $uAttrs = GetFileAttributes( $arg ); $uAttrs==INVALID_FILE_ATTRIBUTES and die "INVALIDE FILE ATTRIBUTES +"; my $ret = '<'; $ret .= $uAttrs&FILE_ATTRIBUTE_ARCHIVE ? 'A' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_COMPRESSED ? 'C' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_DEVICE ? 'D' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_DIRECTORY ? 'd' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_ENCRYPTED ? 'E' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_HIDDEN ? 'H' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_NORMAL ? 'N' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_NOT_CONTENT_INDEXED ? '!' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_OFFLINE ? 'O' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_READONLY ? 'R' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_REPARSE_POINT ? 'J' : ' '; + # Yes, the reparse point agrees with the Path::Tiny/160 `dir /AL` r +esults $ret .= $uAttrs&FILE_ATTRIBUTE_SPARSE_FILE ? 'S' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_SYSTEM ? 'Y' : ' '; $ret .= $uAttrs&FILE_ATTRIBUTE_TEMPORARY ? 'T' : ' '; $ret .= '>'; return $ret; }

    edit: I guess I didn't say how you should update your code... when I was exploring junctions the end of last year, it was to get rid of a similar error in my backup-from-pc-to-NAS script that I run at home; basically, I just check beforehand if the path is a junction (using either isjunc or is_junction -- I forget which I chose, and I'm not at home right now to check), and if it is, I don't try to process that folder. (IIRC, "My Documents" is really "Documents", so when it follows the real one, everything works okay; but that's a bit vague. I may look things up when I get home.) /edit

    for the second question, use the LIST version of system(). If you want your script to continue without waiting for notepad++, use the system(1,...) syntax mentioned in perlport: perl -e "my $f='readme.txt'; system(1, 'c:\usr\local\apps\notepad++\notepad++.exe', $f)"

      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:

      Thanks for your comment

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