Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

Re: CleanPath

by demerphq (Chancellor)
on Sep 05, 2003 at 16:41 UTC ( #289272=note: print w/replies, xml ) Need Help??

in reply to CleanPath

Minor nit around the %SystemRoot% handling. First off why does this only occur with %SystemRoot% is there something special about this variable that I am unaware of? Or does the OS do the correct handling if any %Env% vars are in the path? But more importantly there is a minor bug in that if two identical paths get a %SystemRoot% translation then they arent treated as dupes and both are kept. The following patch resolves this.

--- 2003-09-05 18:32:14.000000000 +0200 +++ 2003-09-05 18:32:43.000000000 +0200 @@ -59,7 +59,7 @@ } warn "is good -- keeping!\n"; push( @GoodPath, $path ); - $GoodPath{uc $path}= $path; + $GoodPath{uc $path} = $GoodPath{uc $dir} = $path; } } @$aPath= @GoodPath;

As I mentioned in the CB im going to use this as a base for capturing %ENV changes after running utility scripts like VCVARS32.BAT and VSVARS32.BAT and committing their changes to the default system enviornment. Thanks a lot, ive been wanting to write the script youve posted and the extension I mention for a while. Now i only need to the latter.


Oh, and I really do think the use Tie::Registry bit should go at the top of the file. Miaow. ;-)


This is a hack i did of tyes code. It runs a batch file and then extracts the info out and compares it to the current enviornment. Any changes are written into the SYSTEM enviornment registry data.

@rem = '--*-Perl-*-- @echo off if "%OS%" == "" goto Win95 perl -x -S "%0" %* if %errorlevel% == 9009 echo You do not have Perl in your PATH. goto endofperl :Win95 perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9 goto endofperl @rem '; #!/usr/bin/perl -w #line 13 use strict; my ($Reg,$UserEnv,$SysEnv); use Win32::TieRegistry ( TiedRef => \$Reg, ArrayValues => 1, Delimiter => "/", ":REG_" ); BEGIN{ $UserEnv= $Reg->{"CUser/Environment/"} or die "Can't open Registry key, CUser/Environment/: $^E\n"; $SysEnv= $Reg->{"LMachine/System/CurrentControlSet/Control/" . "Session Manager/Environment/"} or die "Can't open Registry key, Session Manager/Environment: $ +^E\n"; } exit(Main()); sub ExpandEnv { my( $str )= @_; while( $str =~ /%([^\s=]+)%/ ) { my $repl= $ENV{$1}; if( ! defined( $repl ) ) { warn "%$1% not set in environment -- dropping.\n"; return ""; } $str =~ s//$repl/; } return $str; } sub CleanPath { my( $aPath, $hUser )= @_; my( $path, $dir ); my @GoodPath= (); my %GoodPath= (); while( @$aPath ) { $path= shift(@$aPath); print STDERR qq< "$path"- >; $dir= ExpandEnv( $path ) or next; $dir =~ s#([^:/\\])[/\\]$#$1#; print STDERR qq<is "$dir"; > if $dir ne $path; $path =~ s#([^:/\\])[/\\]$#$1#; if( ! -d $dir ) { warn "does not exist -- dropping.\n"; } elsif( $dir !~ /^([a-z]:|\\\\)/i ) { warn "isn't absolute -- dropping.\n"; } elsif( $GoodPath{uc $dir} ) { warn "is a repeat -- dropping.\n"; } elsif( defined($hUser) && $hUser->{uc $dir} ) { warn "is user-specific -- dropping.\n"; } else { if( $path =~ /^\Q$ENV{SYSTEMROOT}\E/io ) { $path =~ s/^\Q$ENV{SYSTEMROOT}\E/%SystemRoot%/; print STDERR qq<changed to "$path">; } warn "is good -- keeping!\n"; push( @GoodPath, $path ); $GoodPath{uc $path}= $path; } } @$aPath= @GoodPath; } sub SplitSysPath { my( $SysPath, @dirs )= @_; my @SysPath= split( /;/, $SysPath->[0], -1 ); my $dir; foreach $dir ( @dirs ) { if( $dir !~ m#^[a-z]:[/\\]#i ) { die qq<Usage: $0 ["x:\\dir_to_add" [...]]\n>, "Cleans invalid and repeated directories from the system +\n", "and user-specific PATH environment settings.\n", "Prepends any listed directories to the system PATH.\n"; } elsif( ! -d $dir ) { die "No such directory ($dir): $!\n"; } else { warn "Prepending directory ($dir) to system path.\n"; unshift( @SysPath, $dir ); } } return @SysPath; } sub SaveChanges { my( $keyEnv, $keyPath, $avPath, $type )= @_; if( $keyPath->[0] eq join( ";", @$avPath ) && $keyPath->[1] != REG_SZ() ) { warn "\u$type PATH required no changes.\n"; } elsif( @$avPath ) { if( $keyPath->[1] != REG_EXPAND_SZ() ) { warn "\u$type PATH changed from REG_SZ to REG_EXPAND_SZ.\n +"; $keyPath->[1]= REG_EXPAND_SZ() } $keyPath->[0]= join( ";", @$avPath ); $keyEnv->{"/PATH"}= $keyPath or die "Can't set $type PATH in Registry: $^E\n"; warn "\u$type PATH successfully updated.\n"; } elsif( "" ne $keyPath->[0] ) { if( ! delete $keyEnv->{"/PATH"} ) { warn "Can't delete (now-useless) $type PATH ", "from Registry: $^E\n"; } else { warn "Now-empty $type PATH successfully deleted.\n"; } } } sub SaveState { my( $SysPath, $UserPath )= @_; my $UserName= $ENV{USERNAME} || "user"; if( open( TEMP, ">> $ENV{TEMP}\\" ) ) { printf TEMP "On %d/%02d/%02d %02d:%02d:%02d:\n", sub { $_[0]+=1900; $_[1]++; return @_ } ->( (localtime)[5,4,3,2,1,0] ); print TEMP "Old system PATH=$SysPath\n"; print TEMP "Old $UserName PATH=$UserPath\n"; close TEMP; } else { warn "Can't write to $ENV{TEMP}\\ $!\n"; warn "Old system PATH=$SysPath\n"; warn "Old $UserName PATH=$UserPath\n"; } } sub SetParentPath { my( $path )= @_; my $start= tell(DATA) or die "Can't tell(DATA): $!"; open DATA, "+< $0" or die "Can't read self ($0): $!\n"; seek( DATA, $start, 0 ) or die "Can't fseek(DATA,$start,0): $!"; die "Expected :endofperl after __END__ of $0.\n" unless <DATA> =~ /^\s*:endofperl\s*$/i; seek( DATA, 0, 1 ) or die "Can't fseek(DATA,0,1): $!"; if( $path ne $ENV{PATH} ) { warn "Updating current command shell's PATH...\n"; print DATA "set PATH=$path\n"; } truncate DATA, tell(DATA); } sub CleanPathEntries { my $SysPath= $SysEnv->{"/PATH"} or die "Can't read system PATH from Registry: $^E\n"; my @SysPath= SplitSysPath( $SysPath, @ARGV ); my $UserPath= $UserEnv->{"/PATH"} || [ "", REG_EXPAND_SZ() ]; my @UserPath= split( /;/, $UserPath->[0], -1 ); SaveState( $SysPath, $UserPath ); warn "Cleaning user-specific PATH:\n"; CleanPath( \@UserPath ); my %UserPath= map {uc $_, $_} @UserPath; warn "Cleaning system PATH:\n"; CleanPath( \@SysPath, \%UserPath ); SaveChanges( $SysEnv, $SysPath, \@SysPath, "system" ); SaveChanges( $UserEnv, $UserPath, \@UserPath, "user-specific" ); my $path= join ";", map { ExpandEnv($_) || () } @UserPath, @SysPat +h; SetParentPath( $path ); } sub SimpleCleanPath { my ($env,$k)=@_; my %dupe; my @path=grep { !$dupe{uc($_)}++ and -d $_ } split /;/,$env->{$k}; + $env->{$k}=join ";",@path; } sub Main { my $batch=shift @ARGV; if ($batch) { my $cmd=$batch.' >nul 2>&1 && perl -MData::Dumper -e"print Dum +per(\\%ENV)"'; my $res=`$cmd 2>&1`; my $env; if ($res=~s/^\$VAR1 =/\$env =/) { eval $res or die "$@\n$res"; } else { die $res; } my %pathlike=map {$_=>1} qw( INCLUDE LIB PATH ); foreach my $k (keys %$env) { SimpleCleanPath($env,$k) if $pathlike{uc($k)}; if (!exists $ENV{$k} or uc($ENV{$k}) ne uc($env->{$k})) { if ($SysEnv->{"/$k"}) { warn "Updating system key '$k'.\n"; $SysEnv->{"/$k"}=[$env->{$k},$SysEnv->{"/$k"}[1]]; } else { warn "Creating system key '$k'.\n"; $SysEnv->{"/$k"}=[$env->{$k},REG_SZ()]; } if ($UserEnv->{"/$k"}) { if( ! delete $UserEnv->{"/$k"} ) { warn "Can't delete (now-useless) User $k ", "from Registry: $^E\n"; } else { warn "Now useless user $k successfully deleted +.\n"; } } } } } CleanPathEntries(); 0 } __END__ :endofperl


<Elian> And I do take a kind of perverse pleasure in having an OO assembly language...

Replies are listed 'Best First'.
Re^2: CleanPath (updated)
by tye (Sage) on Jan 20, 2007 at 08:06 UTC
    First off why does this only occur with %SystemRoo­t%

    Other environment variables can be put in path components and CleanPath will handle them just fine. What it does special with %SystemRoot% is that if you put a directory in your $ENV{PATH} that is a subdirectory of %SystemRoot% without saying "%SystemRoot%", then CleanPath changes it to use %SystemRoot%. So "CleanPath C:\Windows\system32\trojans" will actually put "%SystemRoot%\trojans" into your path.

    This feature was mostly added because the big motivation for writing this script was that a coworker had written a script to flush $ENV{PATH} changes into the registry and done a bad job of it, not realizing that $ENV{PATH} doesn't match what gets put in the registry for several reasons (notably user-specific path components and expansion of %SystemRoot%) so I wanted to clean this mess up easily (quite a few system had been corrupted by this before anyone noticed). If you use %SystemRoot%, then those entries in your $ENV{PATH} continue to work even if the drive letter for that partition changes on your when you reboot (and I was working in situations where this wasn't that uncommon of a situation); that's why those entries are done that way.

    I wasn't too worried about the bug you pointed out because just running CleanPath twice takes care of it. I didn't include your fix at first because I needed to look over how I was using %GoodPath to convince myself that there were no problems with that fix. I just now incorporated your fix (slightly differently) as I was making some other minor updates. Thanks!

    - tye        

      If you copy the code be sure not to miss the last "h" its on a newline +h ...:  my $path= join ";", map { ExpandEnv($_) || () } @UserPath, @SysPat i think the last should read @SysPath? Thanks MH

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://289272]
and dust plays in a shaft of sunlight...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (2)
As of 2017-03-25 00:53 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (310 votes). Check out past polls.