Catch `Get-ChildItem` errors on a file-by-file basis - powershell

I'm using Get-ChildItem -Recurse to search a directory. I can't guarantee that everything Get-ChildItem will hit will be accessible. I want to log these failures, but not fail the entire Get-ChildItem -Recurse command. Right now I have
Get-ChildItem -Recurse $targetdir -ErrorAction Inquire `
| where { $_.Name -eq $name } `
| foreach {
echo-indented "Found $(hash $_) at $($_.FullName)"
$_
}
The code in question is the -ErrorAction Inquire. If I did -ErrorAction Stop, I would have to put a try-catch somewhere. It would have to be around the entire pipeline, right? In that case, childitems that would have been found after the inaccessible one will not be found and written out. So what else can I do?

For Get-ChildItem -Recurse, specifying the -ErrorAction won't really help you here. It will only cause access deny errors to either be:
Terminating (-ErrorAction Stop) where everything just stops. (not what you want)
Non-terminating (the default -ErrorAction Continue) which is what you want, as it will continue.
As for logs, with the default -ErrorAction Continue, all access denies are logged to the $Error variable. We can then parse through the exception records to get the information that we need:
#Start by clearing the error variable
$Error.Clear()
#Execute Get-ChildItem with -ErrorAction Continue
ls -Recurse $targetdir -ErrorAction Continue `
| where { $_.Name -EQ $name } `
| foreach {
echo-indented "Found $(hash $_) at $($_.FullName)"
$_
}
#Display objects we got Access Denies on:
$Error | ForEach-Object {
Write-Host $_.TargetObject
}

You can use the -ErrorVariable common parameter to save the errors to a variable.
Get-ChildItem -recurse foo -ErrorVariable err
$err

Related

Powershell Foreach Loop Is Using My Files

I have the following code
$top = Get-ChildItem -File C:\SomeFolder\1\ -Recurse -filter *.evtx
foreach ($file in $top) {
$get = (Get-WinEvent -Path C:\SomeFolder\1\$file -erroraction 'silentlycontinue').count
if ($get -eq '0') {
Remove-Item -Path C:\SomeFolder\1\$file -Recurse -Force
}
}
The problem is that the files that I'm trying to delete are in use by the loop.
The following error appears - "Remove-Item : Cannot remove item The process cannot access the file because it is being used by another process."
I even tried exiting with break but it didn't help.
Any suggestions?
Thanks!

Powershell copy-item then test-path or copy-item with catch?

I have a script that moves files around in a production environment and currently performs a copy-item, then test-path, followed by remove-item if the test-path worked ok, similar to the below:
if ($copySuccess -eq $true) {
$files = Get-ChildItem $fileDir -Filter $filePrefix*.*
$files | ForEach-Object {
if ($copySuccess -eq $true) {
Copy-Item $fileDir\$_ -Destination $destDir
if (!(Test-Path $destDir\$_)) {
$copySuccess = $false
}
}
}
}
This method makes me feel comfortable as the test-path guarantees that the file is where it needs to be, before removing it.
I'm planning to rewrite parts of the script and I'm wondering if by using a copy-item with a catch on error, I can be sure that if no error is seen that the file has definitely been copied to the destination (without the need to use test-path as I assume this would make it quicker). As in below:
Get-ChildItem $fileDir -Filter $filePrefix*.* | ForEach {
if ($copySuccess -eq $true) {
try {
Copy-Item $fileDir\$_ -Destination $destDir -ErrorAction Stop
}
catch {
$copySuccess = $false
}
}
}
}
Of course, if there is a better way, please let me know (Powershell v5). The reason for this level of checking is that there are often network issues on the infrastructure, hence the test-path currently in use.
ErrorAction won't work in this case since:
The ErrorAction parameter has no effect on terminating errors (such as
missing data, parameters that are not valid, or insufficient
permissions) that prevent a command from completing successfully.
[Source]
If you want to check whether Copy-Item worked you can do this in a couple of ways to make sure.
The first one is to use $? variable:
Errors and Debugging: The success or failure status of the last
command can be determined by checking $?
Copy-Item $fileDir\$_ -Destination $destDir
if(-not $?) {
Write-Warning "Copy Failed"
}
Another method is by using the -Passthru parameter, we can capture the results to a variable. Note, this variable will only be populated if the operation was successful:
if(-not Copy-Item $fileDir\$_ -Destination $destDir -PassThru) {
Write-Warning "Copy Failed"
}

remove-item -force not working on download

so i had writen this script that will clear out the files in thedownload but it doesn't work
$DaysToDelete = 1
download
Get-ChildItem "C:\users\*\Downloads\*"-Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { ($_.CreationTime -lt $(Get-Date).AddDays(-$DaysToDelete))} |
remove-item -force -Verbose -recurse -ErrorAction SilentlyContinue
This code is valid in what it is supposed to do. If there are any errors with file access or anything else, you can inspect them via $Error
automatic variable

Trying to remove-item but it's not getting removed

I'm thinning out my backup files with a powershell script, and I know I have the correct filenames, but for some reason when I use remove-item, the item doesn't get removed and no exception is thrown. This is what it looks like:
try{
$Drive = "E:\temp\"
$deleteTime = -42
$limit = (Get-Date).AddDays($deleteTime) #files older than 6 weeks
#get files in folder older than deleteTime and with signature of *junk.vhd* (to be changed later)
$temp1 = Get-ChildItem -Path $Drive -filter "*junk.vhd*" | Where-Object {$_.LastWriteTime -lt $limit} | Select -Expand Name #this has 5 files in list
#will delete every other file
for($i=$temp1.GetLowerBound(0);$i -le $temp1.GetUpperBound(0);$i+=2) {
$name = $temp1[$i]
Write-Host "removing $name" #prints correct file names to screen
Get-ChildItem -Path $Drive -include $name | Remove-Item -recurse -force #this is handling correct files but they aren't deleted for some reason
}
}#try
Catch [Exception] {
#nothing is caught
Write-Host "here"
}
Does anyone have any ideas why it's finding and Write-Host the correct filenames to remove, but the Remove-Item isn't removing them?
I was looking at removal a little different example, but everything looks good.
try to replace this line:
Get-ChildItem -Path $Drive -include $name | Remove-Item -recurse -force #this is handling correct files but they aren't deleted for some reason
with this:
Remove-Item $temp1[i].FullName -force -recurse
Since you already got the full patrh to the file, I don't feel it's necessary to call Get-ChilItem again and pass it through the pipeline, instead you just feed Remove-Item with the FUllName property which is the full path to thew file.

Capture the messages by cmdlets in PowerShell

I am running the below script. If the Copy-Item command is successfully completed, it does not show any messages such as how many files are copied. How do I capture this?
Note: I also need to capture the error message which the script is doing correctly.
$LogFile = "P:\users\Logname.log"
$msg = Copy-Item P:\Bkp_20130610\* P:\users -force -recurse -ErrorAction SilentlyContinue
if (-not $?)
{
msg1 = $Error[0].Exception.Message
Write-Host "Encountered error. Error Message is $msg1."
exit
}
$msg > $LogFile
Write-Host "Hello"
You can obtein a list of copied files in this way
$files = copy-item -path $from -destination $to -passthru
pipe it to | ? { -not $_.psiscontainer } if you are copying folder and you don't want them in the count
then use
$files.count
You can use the -Verbose switch with the Copy-Item cmdlet:
$msg=Copy-Item P:\Bkp_20130610\* P:\users -force -recurse -ErrorAction SilentlyContinue -Verbose