I have a script in which Some of the files get deleted...
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem1
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem2
but others throw this error following the other verbose messages:
Cannot remove item d:\temp\name1.db\metadata.sqlitedb: The process
cannot access the file 'metadata.sqlitedb' because it is being used by
another process.
Directory d:\temp\name1.db cannot be removed because it is not empty.
How can i automatically kill the process used (whichever it is) and attempt to remove the item again as part of my script above?
i tried handle suggestion from this thread PowerShell script to check an application that's locking a file?, but i think our servers dont allow external tools as i either dont get any output or getting access denied...so im looking for some other option that doesnt require external tool
Essentially looking for something like this as part of my script:
$Directory = "d:\temp"
Invoke-Command -Computer $Server -ScriptBlock {
param ($dir, $name)
#Write-Output "dir='$dir', name='$name'"
$f = Get-ChildItem -Path $dir | Where {$_.Name -Match $name} | Select -ExpandProperty FullName
if ($f) {
$f | Foreach {
try
{
Remove-Item $_ -confirm:$false -recurse -Verbose #-WhatIf
}
catch
{
if ($_.Exception.Message -like '*it is being used by another process*')
{ write-host "that process is " $pid + $pname
try{
KILL PROCESS
Remove-Item $_ -confirm:$false -recurse -Verbose #-WhatIf
}
catch
{
$error[0]
}
}
else
{
Write-Host "$($error[0])`r`n" -foregroundcolor magenta -backgroundcolor black
}
}
}
}
else {
Write-Verbose "No file found"
}
} -ArgumentList $Directory, $DB -verbose
Related
I am needing to create a logfile for every action that is performed by this script.
I have attempted to use redirection(>>) however, cannot get the output to actually write to the file.
while($true){
$FromMC = Get-ChildItem -Path "\\x\From MC" -Filter *.mpg
Write-Host "Checking '\\x\From MC' for files" -ForegroundColor Cyan
Write-Host $FromMC.count"Files Found" -ForegroundColor Cyan
Write-Host ""
ForEach($file in $FromMC){
try{
Move-Item -Filter 7ST* -Path $file.Fullname -Destination "\\x\programs\7TH STREET THEATER" -Verbose -Force -ErrorAction Stop
}
catch{...}
Write-Host "Pausing for"$ts.Minutes"minutes..." -ForegroundColor Cyan
Write-Host "Ctrl+C to Stop"
Write-Host ""
Start-Sleep -Seconds $ts.TotalSeconds
}
I expect the output to be exactly as "-verbose" outputs into the shell. Types of output: Write-Host, Verbose, Write-Warning
I feel the solution is extremely simple, and I am just overlooking it.
To log everything that would normally be written to the console, like -verbose you can use Start-Transcript and when you are finished Stop-Transcript.
Example:
Start-Transcript -Path "C:\logs.txt"
#run code you want to capture
Stop-Transcript
I have the following script that removes files and any folders matching the name. recurse is needed to avoid confirmation prompt.
Invoke-Command -Computer $Server -ScriptBlock {
param ($dir, $name)
$f = Get-ChildItem -Path $dir | Where {$_.Name -Match "$name"}
If ($f) {
$f | Foreach {
Remove-Item $_.fullname -confirm:$false -Recurse -Verbose
}
}
else {
Write-Verbose "No file found"
}
} -ArgumentList $Directory, $DB
i get a TON of verbose messages for every single one of those items saying
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem1
VERBOSE: Performing the operation "Remove Directory" on target
\name1\subitem2
VERBOSE: Performing the operation "Remove Directory" on target
\name1.db
can i make it so that it just prints verbose on a folder level instead for every single subitem? essentially i would like only an output like this:
VERBOSE: Performing the operation "Remove Directory" on target \name1
VERBOSE: Performing the operation "Remove Directory" on target
\name1.db
Adding -Verbose to Remove-Item will always cause it to list out every item that it is removing (i.e. that's the point of Verbose output. It's a fire hose that's either on or off).
If you want, not necessarily less logging, but filtered logging, then the only real option is to remove -Verbose and do it yourself.
For ex.:
Invoke-Command -Computer $Server -ScriptBlock {
param ($dir, $name)
$f = Get-ChildItem -Path $dir | Where {$_.Name -Match "$name"}
If ($f) {
$f | Foreach {
#Check to see if it is a folder
if($_.PSIsContainer)
{
Write-Host "Removing Directory: $($_.FullName)"
}
Remove-Item $_.fullname -confirm:$false -Recurse
}
}
else {
Write-Verbose "No file found"
}
} -ArgumentList $Directory, $DB
I'm working on a script to test if a file exists in a target directory, and if false, execute an application installation.
The goal is to test if the file exists and if it does, abort with a log as to why. If the file does not exist, then the application installer should be executed.
I first tried creating a dummy file using New-Item to create both the directory and the dummy file.
New-Item -ItemType Directory -Force -Path "C:\temp"
New-Item -Path "C:\temp\vistaupdated.txt" -ItemType "file" -Value "Updated"
# Test if computer is updated
$file = Get-Content "C:\temp\vistaupdated.txt"
$containsWord = $file | %{$_ -match "Updated"}
if ($containsWord -contains $false) { ###start running the install stuff
However, this errors out if the file does not exist with an Object-NotFound. I then decided to switch tactics and use Test-Path:
$file = Test-Path -Path "C:\temp\vistaupdated.txt"
if ($file -eq $false) { ###start running the install stuff
In this case, I believe $file would evaluate to False and as a result execute the installation. On execution all I get is a return of the script's path:
PS C:\users\me\desktop> $filetest = Test-Path -Path "C:\temp\vistaupdated.txt"
PS C:\users\me\desktop> $filetest
False
PS C:\users\me\desktop> C:\Users\me\Desktop\vistaupdate.ps1
The above reference to the PS1 file doesn't execute. It's only what's returned if I run the script through ISE as administrator. If I do the same with the console, then the output is blank without any action taken.
# Test if computer is updated
$file = Test-Path -Path "C:\temp\vistaupdated.txt"
if ($file -eq $False) {
#package repository
$VistaInsPath = "\\apps\shared\me\vista\6.16.0"
#package installation command
$VistaInsEXE = "VistaClient.6.16.0.896"
#package installation parameters
$VistaInsParam = "/s /v/qn"
#logging
$logFile = "\\apps\shared\me\vista\6.16.0\log\vista_install.log"
#timestamp for logging
function Get-TimeStamp {
return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
}
#main function
function Vista-Install {
$errFlag = $false
try {
$cmdLine = "$VistaInsPath$VistaInsEXE"
Start-Process -FilePath $cmdLine -ArgumentList $VistaInsParam -Wait
} catch {
$(Get-TimeStamp) + "[" + $env:COMPUTERNAME +"]" + "[" + $env:USERNAME +"]" + "Error has occurred: " + $_.Exception.Message | Out-File $logFile -Append
$error.Clear()
$errFlag = $true
}
#if no error, notify success
if (-not $errFlag) {
$(Get-TimeStamp) + "[" + $env:COMPUTERNAME +"]" + "[" + $env:USERNAME +"]" + "Completed Successfully" | Out-File $logFile -Append
}
# Place dummy file to tag computer as updated
New-Item -ItemType Directory -Force -Path "C:\temp"
New-Item -Path "C:\temp\vistaupdated.txt" -ItemType "file" -Value "Updated"
}
} else {
$(Get-TimeStamp) + "[" + $env:COMPUTERNAME +"]" + "[" + $env:USERNAME +"]" + "Computer Already Updated. Aborting" | Out-File $logFile -Append
}
If Test-Path is False, then the installer should kick off in the first if statement. Instead the script spits back the path of the script itself and exits.
As #lit already pointed out in the comments to your question, you never invoke the function Vista-Install, so it's rather unsurprising that your code doesn't actually do anything. And you shouldn't define functions (or your $Vista* and $LogFile variables) in a nested scope anyway. The code in the else branch won't be able to find Get-TimeStamp or $LogFile with what you currently have.
Allow me to suggest some improvements:
Your logging code has a lot of redundant information. Instead of defining a function just for generating the timestamp, make a function that takes the log message as a parameter and otherwise completely encapsulates the logging.
$cmdLine = "$VistaInsPath$VistaInsEXE" will combine directory path and filename without a path separator, resulting in an incorrect path. Either put a backslash between the variables:
$cmdLine = "$VistaInsPath\$VistaInsEXE
or (better) use the Join-Path cmdlet, as #Theo suggested:
$cmdLine = Join-Path $VistaInsPath $VistaInsEXE
Put Test-Path directly in the if condition. There's no need to assign the result to a variable first.
The variable $errFlag is pointless. Just put the log statement after Start-Process. If an exception is thrown the code will go to the catch block without reaching that statement.
I assume you want the file vistaupdated.txt created only if the installation didn't throw an error, so that code should go in the try block too.
New-Item outputs an object for the item. You may want to suppress that.
The function Vista-Install doesn't make much sense either, as it would only install one specific program. Since it has very little code to begin with I would just drop it and put the code directly in the "then" branch. But if you wanted it to be a function you should name and parametrize it properly: pass program and arguments as parameters (preferably named after the parameters of Start-Process, so you can simply splat the $PSBoundParameters variable) and use a name conforming to the naming conventions:
function Install-Program {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true, Position=0)]
[string]$FilePath,
[Parameter(Mandatory=$false, Position=1)]
[string]$ArgumentList = #()
)
try {
Start-Process #PSBoundParameters
...
} catch {
...
}
}
Install-Program -FilePath (Join-Path $VistaInsPath $VistaInsEXE) -ArgumentList $VistaInsParam
Simplified code:
$VistaInsPath = '\\apps\shared\me\vista\6.16.0'
$VistaInsEXE = 'VistaClient.6.16.0.896'
$VistaInsParam = '/s', '/v', '/qn'
$logFile = '\\apps\shared\me\vista\6.16.0\log\vista_install.log'
function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string]$Message = ''
)
"[{0:MM/dd/yy HH:mm:ss}]`t[{1}\{2}]`t{3}" -f (Get-Date), $env:COMPUTERNAME, $env:USERNAME, $Message |
Out-File $script:LogFile -Append
}
if (-not (Test-Path -Path 'C:\temp\vistaupdated.txt')) {
$cmdLine = Join-Path $VistaInsPath $VistaInsEXE
try {
Start-Process -FilePath $cmdLine -ArgumentList $VistaInsParam -Wait
New-Item -Path 'C:\temp' -Type Directory -Force | Out-Null
New-Item -Path 'C:\temp\vistaupdated.txt' -Type File -Value 'Updated' | Out-Null
Write-Log 'Completed successfully.'
} catch {
Write-Log ('Error has occurred: {0}' -f $_.Exception.Message)
}
} else {
Write-Log 'Computer already updated. Aborting.'
}
Arguments could be made for flipping "then" and "else" branch to avoid a negated condition:
if (Test-Path -Path 'C:\temp\vistaupdated.txt') {
Write-Log 'Computer already updated. Aborting.'
} else {
$cmdLine = ...
...
}
or exiting from the script directly if the file exists in order to avoid an else branch entirely:
if (Test-Path -Path 'C:\temp\vistaupdated.txt') {
Write-Log 'Computer already updated. Aborting.'
exit
}
$cmdLine = ...
...
I'm trying to create a powershell script that deletes user profiles older than 30 days with powershell and excluding some users like admins accounts.
I've though maybe the script has to be signed by the domaincontroller or something, but i'm not sure if that will be the solution.
When i try to run it on a other directory it works but when i use it on c:\Users i get an error
Does anyone know what i have to change?
Error:
Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is overridden by a
policy defined at a more specific scope. Due to the override, your shell will retain its current effective execution
policy of RemoteSigned. Type "Get-ExecutionPolicy -List" to view your execution policy settings. For more information p
lease see "Get-Help Set-ExecutionPolicy".
At line:1 char:46
+ ... -ne 'AllSigned') { Set-ExecutionPolicy -Scope Process Bypass }; & 'H ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Set-ExecutionPolicy], SecurityException
+ FullyQualifiedErrorId : ExecutionPolicyOverride,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand
The Code:
$Now = Get-Date
$Days = "15"
$TargetFolder = "C:\Users"
$LastWrite = $Now.AddDays(-$Days)
$Folders = get-childitem -path $TargetFolder |
Where {$_.psIsContainer -eq $true} |
Where {$_.LastWriteTime -le "$LastWrite"}
foreach ($Folder in $Folders)
{
if($Folder -notlike "user1")
{
if($Folder -notlike "Administrator")
{
if($Folder -notlike "user2")
{
if($Folder -notlike "Public")
{
if($Folder -notlike "NetworkService")
{
if($Folder -notlike "LocalService")
{
if($Folder -notlike "user3")
{
if($Folder -notlike "user4")
{
write-host "Deleting $Folder" -ForegroundColor Green
Remove-Item -recurse -Force C:\Users\$Folder
#Write-Host -NoNewLine "Press any key to continue... `n";
#$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
else
{
write-host "Cannot delete $Folder" -ForegroundColor Red
}
}
Here is my script which I have used sometime ago:
Most code is commented, so that should be clear. If you have any questions please ask.
# ********************************************************************************************************(**
# * **
# * Short description: Check profiles if they can be deleted - unused profiles will be deleted. **
# * Full description: * User running this script can specify the time which will check if profile contains **
# * any newer files than limit. If yes such profile is skipped. **
# * * User can specify which directories will be excepted from this search **
# * * User can specify which file suffixes will be ignored when the date will be checked **
# * * User can specify custom path for the profiles **
# * **
# * Creator : Patrik Svestka **
# * Created : 21/08/2017 **
# * Version : 1.0.1 **
# * **
# * Changes description: 1.0.0 - First Public version - Init release **
# * 1.0.1 - Added license type, minor changes to the header **
# * **
# * PowerShell compatibility: 2.0 , 4.0 and probably newer (untested) **
# * PowerShell tested versions: v2.0.50727, v4.0.30319 **
# * **
# * License: MIT **
# * **
# * TODO: ability to run the script remotely **
# to test remote connection - Get-WmiObject -ComputerName <server_name> Win32_Service -Credential $credentials
# Or manually from PowerShellEnter-PSSession <server_name> -Credential domain\<user_id>
# ***********************************************************************************************************
# **********************************************************
# Test run?
# **********************************************************
# when you want to test what will be deleted
$test_run = $true;
If ($test_run) {
Write-Warning -message 'Test run ENABLED - for actual DELETION set $test_run to $false' -verbose;
"`n";"`n";
}
# **********************************************************
# User configuration
# **********************************************************
# $credentials = 'domain\<user_id>';
# $server_name = '<server>';
# Profiles that contain file newer than 90 days will be exempted from deletion
$time_definition=#{'1m'="-0"};
# TODO: test for more periods - not tested yet!
# e.g more time frames - $time_definition=#{'1m'="-30"; '3m'="-90"; '6m'="-180"; '12m'="-360"; '18m'="-540"}
# running script path
$current_path = (Resolve-Path .\).Path;
$log_file = "$($current_path)\delete_files.log";
$folder_to_cleanse = 'E:\t\temp_profiles\'; #'C:\prg'
$excluded_directories = [System.Collections.ArrayList]#();
# All excluded profiles:
$excluded_directories.Add('All Users') | Out-null;
$excluded_directories.Add('Administrator') | Out-null;
$excluded_directories.Add('Default User') | Out-null;
$excluded_directories.Add('LocalService') | Out-null;
$excluded_directories.Add('NetworkService') | Out-null;
# Extensions excluded from date validation - these files will not influence the date check
# (will be deleted too if all others are found older)
$excluded_file_types = [System.Collections.ArrayList]#();
#$excluded_file_types.Add("*.bat", "*.cmd", "*.ps1") | Out-null;
$profile_directories = [System.Collections.ArrayList]#();
# **********************************************************
# The script's start
# **********************************************************
$newer_file_exist = $Null;
$files_to_delete = $Null;
# If previous log file exists delete it (only during test run)
If ((Test-Path -Path "$log_file") -and ($test_run)) {
Write-Verbose "Deleting previous log file $log_file." -verbose;
Remove-Item $log_file
}
# get all directories except excluded ones
$profile_directories = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True} | % { $_.Name }
# if $profile_directories found to be deleted => exit
If ([String]::IsNullOrEmpty($profile_directories)) {
Write-Warning -message "No profile directories to delete. Exiting." -verbose;
Exit;
}
# search in profile directories that are left after exclusion
# for all periods defined in time_definition
ForEach ($profile in $profile_directories) {
ForEach ($time in $time_definition.GetEnumerator()) {
Write-Verbose -message "Now processing the following profile: $folder_to_cleanse$profile." -verbose;
$test_current_pathPath = Test-Path -Path "$folder_to_cleanse$profile";
If ($test_current_pathPath) {
# check if any newer than $time_definition are present within the profile structure
# LastAccesstime can be empty! It is better, less issues, to use LastWriteTime. If you must use LastAccessTime use a check for ::IsNullOrEmpty
# LastWriteTime must be greater than current day - $time.Name (e.g. -90 days)
$newer_file_exist += Get-ChildItem -Path "$folder_to_cleanse$profile" -recurse -Force -exclude $excluded_file_types | Where-Object {$_.PSIsContainer -eq $FALSE} | where {($_.LastWriteTime).ToString('yyyy-MM-dd') -gt (get-date).adddays($time_definition.$($time.Name)).ToString('yyyy-MM-dd')};
}
# if any new file than the limit found the whole profile directory will be skipped (testing if $newer_file_exist $null)
If ($newer_file_exist) {
# add the top directory into excluded directory
$excluded_directories.Add($profile) | Out-null;
$newer_file_exist=$Null;
Write-Verbose -message "The profile $profile will be excluded from deletion process." -verbose;
continue;
}
}
}
# excluding the directories with newer files than limit defined by user
$profiles_with_path = Get-ChildItem -Path $folder_to_cleanse -exclude $excluded_directories | Where-Object {$_.PSIsContainer -eq $True}
# perhaps all $directories are now excluded?
If ([String]::IsNullOrEmpty($profiles_with_path)) {
Write-Warning -message "No directories to delete all probably filtered. Exiting." -verbose;
Exit;
}
# get all files to be deleted
ForEach ($dir in $profiles_with_path) {
# to check
$test_current_pathPath = Test-Path -Path $dir
If ($test_current_pathPath) {
#write-host 'Currently writing for these months:'$($time.Name);
$files_to_delete += Get-ChildItem -Path $dir -recurse -Force | Where-Object {$_.PSIsContainer -eq $FALSE} | % { $_.FullName }
}
}
# **********************************************************
# Messages for the user
# **********************************************************
Write-Verbose -message "List of profiles to be deleted:" -verbose;
ForEach ($profile_to_delete in $profiles_with_path) {
Write-Verbose -message "$profile_to_delete`n" -verbose;
}
Write-Verbose -message "The total count of non-excluded profile directories: $($profiles_with_path.Count)" -verbose;
Write-Verbose -message "==========================`n`n" -verbose;
Write-Verbose -message "List of excluded directories:`n" -verbose;
ForEach ($excluded_profile in $excluded_directories) {
Write-Verbose -message "$folder_to_cleanse$excluded_profile`n" -verbose;
}
Write-Verbose -message "Total count of excluded directories: $($excluded_directories.Count)" -verbose;
Write-Verbose -message "==========================`n`n" -verbose;
Write-Verbose -message "Total directory count (both to be deleted and excluded): $($($profiles_with_path.Count)+ $($excluded_directories.Count))`n" -verbose;
# **********************************************************
# Test run or actual deletion process
# **********************************************************
If ($test_run) {
ForEach ($file in $files_to_delete) {
$file | Out-file -Encoding 'Unicode' -FilePath $log_file -Append # >> $log_file
}
Write-Verbose 'This number of files would be deleted:' -verbose;
Write-Verbose "Found $($files_to_delete.Count) files marked for deletion." -verbose;
} Else {
$files_deleted = 0;
# delete files
If ($files_to_delete) {
ForEach ($file in $files_to_delete) {
#Remove-Item $file -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item $file -Force -ErrorAction SilentlyContinue
If ($? -eq $true) {
$files_deleted ++;
#Write-Verbose -Verbose "$File deleted successfully!"
}
}
}
# delete directories
$directories_deleted = 0;
ForEach ($dir in $profiles_with_path) { #
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
If ($? -eq $true) {
$directories_deleted ++;
#Write-Verbose -Verbose "$File deleted successfully!"
}
}
Return "Total files to be deleted: $($files_to_delete.count)","Total files Deleted: $files_deleted", "Total Directories deleted: $directories_deleted"
}
I am trying to clear out some orphaned user shares on a DFS share. I want to compare the full name of the folder to the HomeDirectory property of a specified object using Get-ADUser -Filter.
If I use for instance (Get-ADUser $varibale -Properties * | Select Homedirectory) I get an error displayed when the account is not found. So I used -Filter to hide the error if there is no account found. However, this is much slower than the -Properties * | Select method.
Script:
$path = Read-Host -Prompt "Share path...."
$Dirs = Get-ChildItem $Path
foreach ($D in $Dirs) {
$Login = Get-ADUser -Filter {HomeDirectory -eq $d.FullName}
if ($d.FullName -ne $Login."HomeDirectory") {
$host.UI.RawUI.WindowTitle = "Checking $d..."
$choice = ""
Write-Host "Comparing $($d.FullName)......." -ForegroundColor Yellow
$prompt = Write-Host "An account with matching Home Directory to $($d.FullName) could not be found. Purge $($d.fullname)?" -ForegroundColor Red
$choice = Read-Host -Prompt $prompt
if ($choice -eq "y") {
function Remove-PathToLongDirectory {
Param([string]$directory)
# create a temporary (empty) directory
$parent = [System.IO.Path]::GetTempPath()
[string] $name = [System.Guid]::NewGuid()
$tempDirectory = New-Item -ItemType Directory -Path (Join-Path $parent $name)
robocopy /MIR $tempDirectory.FullName $directory
Remove-Item $directory -Force
Remove-Item $tempDirectory -Force
}
# Start of Script to delete folders from User Input and Confirms
# the specified folder deletion
Remove-PathToLongDirectory $d.FullName
}
} else {
Write-Host "Done!" -ForegroundColor Cyan
}
Write-Host "Done!" -ForegroundColor Cyan
}
You have a couple of suboptimal things in your code (like (re-)defining a function inside a loop, or creating and deleting an empty directory over and over), but your biggest bottleneck is probably that you do an AD query for each directory. You should be able to speed up your code considerably by making a single AD query and storing its result in an array:
$qry = '(&(objectclass=user)(objectcategory=user)(homeDirectory=*)'
$homes = Get-ADUser -LDAPFilter $qry -Properties HomeDirectory |
Select-Object -Expand HomeDirectory
so that you can check if there is an account with a given home directory like this:
foreach ($d in $dirs) {
if ($homes -notcontains $d.FullName) {
# ... prompt for deletion ...
} else {
# ...
}
}
Performance-wise I didn't notice a difference between the robocopy /mir approach and Remove-Item when deleting a 2 GB test folder with each command. However, if you have paths whose length exceeds 260 characters you should probably stick with robocopy, because Remove-Item can't handle those. I would recommend adding a comment explaining what you're using the command for, though, because the next person reading your script is probably as confused about it as I was.
$empty = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid())
New-Item -Type Directory -Path $empty | Out-Null
foreach ($d in $dirs) {
if ($homes -notcontains $d.FullName) {
# ... prompt for confirmation ...
if ($choice -eq 'y') {
# "mirror" an empty directory into the orphaned home directory to
# delete its content. This is used b/c regular PowerShell cmdlets
# can't handle paths longer than 260 characters.
robocopy $empty $d.FullName /mir
Remove-Item $d -Recurse -Force
}
} else {
# ...
}
}
Remove-Item $empty -Force
There's also a PowerShell module built on top of the AlphaFS library that supposedly can handle long paths. I haven't used it myself, but it might be worth a try.