When that runs it delets everything in the current directory. Here is the output.
NTUSER.DAT as of 5555555 Profile of User = . as of is deleted NTUSER.DAT as of Profile of User = . as of is deleted
I need to be able to delete SPECIFIC older profiles with a script(vbs, bat, kix, au3 are all acceptable). Any suggestions?
Code:
;************************************************************************ ; ;DelMachineProfiles ; ;************************************************************************ ;Script to delete old profiles * ; * ;written by J.Maassen (www.JoeM.de) * ; * ;created on 11th october 2005 * ; * ;last time changed 12.oct.2005 18:10 utc * ;************************************************************************
$LPath="C:\Documents and Settings" ;path where the profiles are located $Div = 31 ;number of days, the ntuser.dat which is going to be deleted should not written $Result = ""
Del $LPath + "\*.*" /c /f /h ;delete files in Folder root
$User = DIR($LPath) ;read folder name
while $User <> "" and @ERROR = 0 ;------------------------------------------------------------------- ;do not delete profile of some special users ;maybe this has to be changed on OS with an other language ;please check to adapt ;------------------------------------------------------------------- if $User <> "Default User" AND $USER <> "All Users" AND $User <> "." AND $User <> ".." $Result = GetFileTime($Lpath + $User + "\NTUSER.DAT") ;get date of file NTUSER.DAT $ResultY = Substr ($Result,1,4) $ResultM = Substr ($Result,6,2) $ResultD = Substr ($Result,9,2) $ResultS = VAL($ResultY) * 364 + VAL($ResultM) * 30 + VAL($ResultD) ;count a number of that date ?"NTUSER.DAT as of " + $Result $DateY = Substr (@Date,1,4) $DateM = Substr (@Date,6,2) $DateD = Substr (@Date,9,2) $DateS = VAL($DateY) * 364 + VAL($DateM) * 30 + VAL($DateD) ;count a number of the actual date ;------------------------------------------------------------------- ;I used the 364 and the 30 to have better a smaller number and maybe keep a profile one or two days longer ;better than deleting a profile too early ;------------------------------------------------------------------- if $ResultS < $DateS - $Div ;Filedate older than actualdate minus $Div $DPath = $Lpath + $User DeleteFolder ($DPath) ;next step! delete folderstructure ;write log ?"Profile of User = " + $user + " as of " + $Result + " is deleted" endif endif $User = DIR() Loop
;************************************************************************ ;Script to delete a whole Folderstructure even if it is not empty * ; * ;written by J.Maassen (www.JoeM.de) * ; * ;created on 11th october 2005 * ; * ;last time changed 12.oct.2005 18:10 utc * ;************************************************************************
Dim $file Del ($path + "\*.*") /c /f /h /s ;delete all files in folder structure $file = Dir($path + "\*.*") ;read folder name, there are no more any files in the folders While $file <> "" AND @ERROR = 0 ;no error = subfolders exist If $file <> "." AND $file <> ".." ;do not use the fist two folders SetFileAttr($path + "\" + $file, 128) ;set folder attributes to normal DeleteFolder ($path + "\" + $file) ;therefor that it is a subfolder call function recursively RD ($DPath + "\" + $file) ;delete the folder (it is an empty folder, at least now) EndIf $file = Dir() ;read next folder name Loop RD $path ;after all subfolders are deleted, delete the starting folder
I have several remote machines that people are allowed to use for a week or two at a time, as you can imagine these computers are full of old unused profiles. From time to time I will need to remove the unused profiles to make room for new ones. I don't want a logon script because I don't want the profiles removed until I am sure it's safe to do so.
What I need is a script that sits on my desktop and waits for me to click it. When activated it will find the machines I have defined in the script, find their profile lists, delete all but the profiles but the ones I specify (Admin, All Users...).
Why not at logon, if you delete the profiles older than a certain amount of time? I use a cleanup script at logon, which 'cleans' profiles for a part: we keep 'Local Settings\Application Data' and the ntuser.* files.
We use roaming profiles and Outlook 2003 (ahumm) as a mail client and if we completely remove the profile, Outlook gives an error when a older user (profile dir earlier deleted) logges on. Somehow Outlook needs some files in the 'Local Settings\Application Data' directory.
I think you're getting an error because outlook profiles and xp profiles are kept in different directories.
I can't use a logon script because my users don't have admin priviliges. Even if they did I would have to add each one to the list of "do not deletes" in the script before they logged on. I think it would be eaiser to just wait intil a set of users doesn't need to logon on any more then delete them all at once.
To put it simply I need a script that will: go to a remote machine that I specify open a directoy delete any files in that directory except the ones I specify (all users, admin, my.profile)
for each $usersid in $profiles if $usersid<>'' and right($usersid,4)<>'-500' $username=sidtoname($usersid) $username=right($username,len($username)-instrrev($username,'\'))
;I added this********* if $username='my.profile' exit endif ;*********************
if $username='' $username='account unknown: '+$usersid endif $profiledir=expandenvironmentvars(readvalue($profilelist+'\'+$usersid,'ProfileImagePath')) if $profiledir<>'' $userprofiles=dirlist($profiledir+'.*',1) for each $userprofile in $userprofiles $profiledate=getfiletime($userprofile+'\ntuser.dat') $profiledate=left($profiledate,10) if datemath($profiledate,@DATE)>$maxprofileage $shellcommand='rd /s /q '+$userprofile shell '%COMSPEC% /c '+$shellcommand if $userprofile=$profiledir $retcode=delkey($profilelist+'\'+$usersid) endif endif next endif endif next
Okay, who is deciding when or how the age of the profile is decided? You want to decide on something like 90 days, 120 days? or are you manually deciding what is old?
Do you need to support NT4 style profiles that are stored typically under C:\WinNT\Profiles
$files = Dirplus('\\ [i]path[/i] \documents and settings\') for each $file in $files do ?'Would you like to delete this profile? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' del '\\[i]path[/i]\documents and settings\$file' endif next
Here is some example code WARNING! USE AT YOUR OWN RISK!!
This UDF does not check to verify if the remote machine is running or if you have Admin rights on the system which you must have. It does not check who is logged on or if anyone is accessing any of the files within the profile. It simply checks the last write time against the current time and determines how old the NTUSER.DAT file is.
You can set the age to any date you want for removing the profiles.
PARAMETERS $Days = Number of days before the profile will be considered OLD $sComputer = The name of the remote computer, if left blank the current machine is used $Del = Flag to indicate if you want to remove the profile or simply display the profile. A 1 would mean to delete the profile.
Again - Test and Use at your own risk I highly recommend using a Virtual machine or other Test machine first to verify this code operates as expected.
I have done minimal testing and it appears to work, but due to the nature of this type of code any errors could be quite dangerous.
I have made some change to not write four times the same block. I also add some deletions in the registry if entries point to a non existant directory.
Code:
Break On
Dim $SO $SO=SetOption('Explicit','On') $SO=SetOption('NoVarsInStrings','On') $SO=SetOption('NoMacrosInStrings','On')
;?"== local ==" ;RemoveOldProfiles(365,'',0) ; ;?"== remote ==" ;RemoveOldProfiles(365,'remote computer name',0) ; ;change the 0 to a 1 and uncomment code below where shown to actually remove the profiles
dim $x ? "== terminated ==" gets $x
Function RemoveOldProfiles($Days, Optional $sComputer, $Del) Dim $ProfilePath, $SystemRoot, $SystemDrive
;-- scan profile dir -- If (GetFileAttr($ProfilePath) & 16) ;-- scan profile directory -- Dim $file, $ProfileDir, $Age
$file = Dir($ProfilePath) While $file <>'' If ($file<>".") and ($file<>"..") and ($file<>"All Users") And ($file<>"Default User") And ($file<>"'LocalService") And ($file<>"NetworkService") $ProfileDir = $ProfilePath+"\"+$file If Exist($ProfileDir+"\"+'NTUSER.DAT') $Age = DateCalc(@Date,SubStr(GetFileTime($ProfileDir+"\"+'NTUSER.DAT'),1,10)) If $Age > $Days ? "delete directory : "+$ProfileDir+" : "+$Age
I wrote something very similar about 6 months ago without any delete, just to log information about older profiles.
The RemoveOldProfiles function can be completed with other scans. For example, with SMS 2, some logs files should be deleted for the user associated to the profile dir : with the profile dir, i find the SID which the registry key. then, i convert the SID to the account name finally, i look for the SMS complete log files concerning this user.
OK Doc Sorry, I was just giving a direction he could go without having to base the deletion on a time frame
This will display each existing profile in documents and settings and offer you the choice to delete it or not. you would have to change the parameters of the UDF to the url for a remote system and you would have to have admin permissions on that system (maybe you would want to map a drive to the c:\docs and settings' directory of that system).
Here's the Working code. I tested it locally with a test profile.
Case Left($temp,1) = "m" $maskarray = Split(Right($temp,-2),"|") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask in $maskarray if instr($file.name,$mask) $masktrue=1 " + "EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code Case Left($temp,1) = "f" $maskarray1 = Split(Right($temp,-2)," ") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask1 in $maskarray1 if substr($file.name,Instrrev($file.name,'.')+1)" + "=$mask1 $masktrue=1 EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code
For Each $file In $f.subfolders If Execute($code) $tarray[$i] = $file $i = $i + 1 ReDIM preserve $tarray[$i] EndIf If $sfflag $temp = dirplus($file,$options,$file,$sfflag-1) For Each $item In $temp $tarray[$i] = $item $i = $i + 1 ReDIM preserve $tarray[$i] Next EndIf Next For Each $file In $f.files If Execute($code) $tarray[$i] = $file $i = $i + 1
I found that I had to remove this code when tesing here as there are profiles with a "." in the name.
If ($file<>".")
I'll try to take a further look at your example Christopher at work and see how it works out.
Does it also support the NT4 profiles?
Wasn't really trying to Golf it down so much as get it to actually work correctly. As for SMS, I'm sure that not the only application out there that doesn't appreciate a profile just being rudely removed.
Also, I've found instances of profiles that were not in the Registry so I've stopped relying on the registry as the source of determining all profiles.
No problem jadewith, just saying that you should have a lot of checks in place to make sure you don't delete the wrong thing. I'll have to look at your solution further to see if it supports the different folder locations.
at the beginning, my script was written for NT 4. with your code, i have made changes so that it also works on recent OS.
sure that SMS is not the only application that doesn't appreciate profile removal. it's just an example. but I mention it because we have the problem. if we delete a profile without deleting the SMS log files, publications are not posted again to the user and he has no specific application.
i also verify profiles in the registry because sometime, when a user logs on a workstation, his profile is created with a .000 (or .001, .002, ...). If I clear properly the profiles directory AND the registry, i have not the problem.
Quote: just saying that you should have a lot of checks in place to make sure you don't delete the wrong thing.
I totally agree!
Maybe something like...? Code:
$files = Dirplus('c:\documents and settings') for each $file in $files
If $file = 'Administrator' next endif ; along with added "IF" statements for profiles you are not ever going to delete
do ? $file ?'Would you like to delete the profile '$file' ? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' do ? $file ? 'Are you sure you want to delete this profile? Y/N>' gets $confirm until $confirm
NEXT can't be used in that manner. only one NEXT can exist for each FOR
you could however do something like: Code:
$files = Dirplus('c:\documents and settings') for each $file in $files If not instr($file,'Administrator') ?'Would you like to delete the profile '$file' ? y/n>' gets $deleteIt if $deleteIt='y' ? 'Are you sure you want to delete this profile? Y/N>' gets $confirm endif if $confirm = 'y' rd $file endif endif next
I must be half retarted because I can't get either one of these to work.
NTDOC: I uncommented everything but was unsure which 0 to make a 1 or which $Del needed a 1.
Jadewith: Your code asked me y/n to delete, but I couldn't get it to delete anything. I tried changing rd $file to shell '%ComSpec% /c rd /s /q "$file" >nul 2>nul' but still no luck.
Case Left($temp,1) = "m" $maskarray = Split(Right($temp,-2),"|") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask in $maskarray if instr($file.name,$mask) $masktrue=1 " + "EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code Case Left($temp,1) = "f" $maskarray1 = Split(Right($temp,-2)," ") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask1 in $maskarray1 if substr($file.name,Instrrev($file.name,'.')+1)" + "=$mask1 $masktrue=1 EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code
For Each $file In $f.subfolders If Execute($code) $tarray[$i] = $file $i = $i + 1 ReDIM preserve $tarray[$i] EndIf If $sfflag $temp = dirplus($file,$options,$file,$sfflag-1) For Each $item In $temp $tarray[$i] = $item $i = $i + 1 ReDIM preserve $tarray[$i] Next EndIf Next For Each $file In $f.files If Execute($code) $tarray[$i] = $file $i = $i + 1
$files = Dirplus('c:\documents and settings') for each $file in $files
do ? $file ?'Would you like to delete the profile '$file' ? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' SHELL %COMSPEC%+' /C RD "' + $file +'" /S /Q' ;rd $file ;shell '%ComSpec% /c rd /s /q "$file" >nul 2>nul' endif next
That code will clean eveything out of the profile but gives the ntuser.dat file is in use error and it leaves the user.profile folder in the documents and settings directory.
Quote: Jadewith: Your code asked me y/n to delete, but I couldn't get it to delete anything. I tried changing rd $file to shell '%ComSpec% /c rd /s /q "$file" >nul 2>nul' but still no luck.
That's because "RD" only works on empty directories. (Big DUH on my part for testing with an empty directory)
Here is adjusted code with a single conformation and a little help from Jooel Code:
break on
Dim $SO dim $files dim $file dim $deleteIt dim $confirm
Case Left($temp,1) = "m" $maskarray = Split(Right($temp,-2),"|") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask in $maskarray if instr($file.name,$mask) $masktrue=1 " + "EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code Case Left($temp,1) = "f" $maskarray1 = Split(Right($temp,-2)," ") $codeweight = $codeweight + 1 $code = "$masktrue=0 for Each $mask1 in $maskarray1 if substr($file.name,Instrrev($file.name,'.')+1)" + "=$mask1 $masktrue=1 EndIf Next If $masktrue $weight=$weight+1 endif" + @CRLF +$code
For Each $file In $f.subfolders If Execute($code) $tarray[$i] = $file $i = $i + 1 ReDIM preserve $tarray[$i] EndIf If $sfflag $temp = dirplus($file,$options,$file,$sfflag-1) For Each $item In $temp $tarray[$i] = $item $i = $i + 1 ReDIM preserve $tarray[$i] Next EndIf Next For Each $file In $f.files If Execute($code) $tarray[$i] = $file $i = $i + 1
for each $file in $files If not instr($file,'Administrator') $file='"'+$file+'\"' do ? $file ?'Would you like to delete this profile? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' do ? $file ? 'Are you sure you want to delete this profile? Y/N>' gets $confirm until $confirm endif if $confirm = 'y' SHELL '%COMSPEC% /C RD ' + $file+' /S /Q' ?@error @serror sleep 1 endif endif $deleteIt = '' $confirm = '' next
I think the problem is you're trying to edit the Function which you DON'T DO. (except to uncomment the DEL part)
You simply tell the script what to do, don't change anything else in the function.
The issue with the DirPlus method is that I don't think there are sufficient checks on the actual profiles which are good which are old etc...
Any of these 3 methods can be made to work I'm sure, but I know my posted version works as I've run it. I'm reasonably sure that Christophe's works too as he has done a lot of coding.
The method posted by Jadewith can work too, but currently I just don't think it has enough sense in it to do correctly.
I'll review my code and Christophe's code to combine the best of both and repost an updated version, though I do know that my already posted version works so not sure how you're calling it. Without modifying my script can you get it to show the profiles it WOULD remove?
Okay, reading Jade solution I see that it is not exceptable for most Admins, but may work if all you want/need to support is that folder structure.
1. If folder is located on D: E: etc won't work 2. If folder has been renamed won't work 3. Will not support WinNT upgraded systems with profiles typically in C:\WinNT\Profiles 4. Does not address registry cleanup like Christophe's does.
Code:
$files = Dirplus('c:\documents and settings')
for each $file in $files If not instr($file,'Administrator') $file='"'+$file+'\"' do ? $file ?'Would you like to delete this profile? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' do ? $file ? 'Are you sure you want to delete this profile? Y/N>' gets $confirm until $confirm endif if $confirm = 'y' SHELL '%COMSPEC% /C RD ' + $file+' /S /Q' ?@error @serror sleep 1 endif endif $deleteIt = '' $confirm = '' next
Last try with a better conformation of deletion Code:
call dirplus.udf Dim $SO dim $files dim $file dim $deleteIt dim $confirm dim $confirmIt dim $rc $rc=srnd(@mdayno) $SO=SetOption('Explicit','On') $SO=SetOption('NoVarsInStrings','On') $SO=SetOption('NoMacrosInStrings','On')
$files = Dirplus('c:\documents and settings')
for each $file in $files If not instr($file,'Administrator') and not instr($file,'All Users') ;...and not instr($file,'any other profile') $file='"'+$file+'\"' $confirm=rnd($rc) ;sets a random conformation number for later do ? $file ?'Would you like to delete this profile? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' do ? $file ? 'If you sure you want to delete this profile' ? 'type the number you see here '+$confirm+' >'gets $confirmit ;has you enter a conformation number to delete until $confirmit Select case $confirmit = $confirm ;deletes the profile in question SHELL '%COMSPEC% /C RD ' + $file+' /S /Q' ?@error @serror sleep 1 case $confirmit <> $confirm ? 'Comformation incorrect! Action Cancelled!' ? $file+'Was not removed' sleep 1 endselect endif endif cls $deleteIt = '' $confirm = '' $confirmit = '' next
Quote: 1. If folder is located on D: E: etc won't work 2. If folder has been renamed won't work 3. Will not support WinNT upgraded systems with profiles typically in C:\WinNT\Profiles 4. Does not address registry cleanup like Christophe's does.
Ok Doc, You are OBVIOUSLY a better KiXtart coder than me. I know that and I bow to it. But I would like to address your four points.
1.& 3. If you change the parameter in the line $files = dirplus() You can send the script anywhere you want. ie: c:\winnt\profiles or \\somecomputer\e$\documents and settings 2. If the Network Admin doesn't know that the directory holding the profiles has been renamed, You've got WAY bigger problems than this script not working. 4. You are absolutly correct. But then I know I'm not good enough a KiXtarter to go suggesting registry hacks to ANYONE. I leave that to you and the other moderators.
I know my script doesn't just go in, delete directories, and get out. It seemed to me though that what he was looking to do was pick and choose which profiles to delete whenever he ran the script.
Please don't take offense, on the contrary I think this post will help you to code better.
There are guys here that can code much better than I, but the point is that with practice we all get better at it.
For item #2 actually if you rename the folder location the registry will automatically pick that up and all your other applications will be happy. The point being is to whenever possible stay away from HARD CODING a path or other information unless there just is no other valid way around it.
Sorry if you felt I was picking on you - did not mean for it to sound that way. Just wanted you to search further into your coding to see WHY there could be issues, and then code for those issues if possible.
Please don't feel discouraged or that I'm picking on you and continue to post help and suggestions for others.
I still make plenty of mistakes in my coding and I've learned quite a bit over time from others pointing out my mistakes.
And if you change it to this you can send the script wherever you want Code:
call dirplus.udf Dim $SO dim $files dim $file dim $deleteIt dim $confirm dim $confirmIt dim $rc dim $path $rc=srnd(@mdayno) $SO=SetOption('Explicit','On') $SO=SetOption('NoVarsInStrings','On') $SO=SetOption('NoMacrosInStrings','On')
do ?'Please enter the path';enter the path to the directory that holds the profiles ?'>' gets $path until $path
$files = Dirplus($path)
for each $file in $files If not instr($file,'Administrator') and not instr($file,'All Users') ;...and not instr($file,'any other profile') $file='"'+$file+'\"' $confirm=rnd($rc) ;sets a random conformation number for later do ? $file ?'Would you like to delete this profile? y/n>' gets $deleteIt until $deleteIt if $deleteIt='y' do ? $file ? 'If you sure you want to delete this profile' ? 'type the number you see here '+$confirm+' >'gets $confirmit ;has you enter a conformation number to delete until $confirmit Select case $confirmit = $confirm ;deletes the profile in question SHELL '%COMSPEC% /C RD ' + $file+' /S /Q' ?@error @serror sleep 1 case $confirmit <> $confirm ? 'Comformation incorrect! Action Cancelled!' ? $file+'Was not removed' sleep 1 endselect endif endif cls $deleteIt = '' $confirm = '' $confirmit = '' next
You have helped me loads in the past, and I respect your abilities. I was being sincere with what I said before. OK maybe I was a little flustered, but not offended.
Okay I did some testing and Christophe Melin's code seems to work as expected on XP and NT4 / Local and Remote.
A bit odd looking compared to how I code, but none the less after reviewing it - it does appear to work correctly. That REPLACE code was initially a bit misleading for me, but I see how it is working now. Good job Christophe.
So there Mathhewst, at least 2 versions that work and the 3rd one by Jade should also work overall, didn't do any testing of that one though as noted elsewhere in this posting.
The confirmation number script is "flippin sweet", but for simplicity in automation I decided to use the script from post #161870. Slightly modified to exclude my profile and a few others.
I couldn't have done it without you guys, Thanks!!