I have a simple script to delete a file, I know it could be more robust, but here it is
$LogFile = ".\deleteRegPol.log"
try
{
Remove-Item "c:\test\new text document.txt" -force
Add-Content $LogFile -Value "We ran the command"
}
catch [Exception]
{
Add-Content $LogFile -Value $_
}
finally
{
}
When the file I am trying to delete doesn't exist, I get an error on the command line but in my log file, it says the command ran. This is telling me that an exception was not thrown resulting in the flow going to the catch block. Why not?
PowerShell does not normally throw an exception when there is an error. Instead it writes a record to the error stream, which you can redirect. To force it to throw there are two options. One is to set the global error preference to stop:
$ErrorActionPreference = "Stop"
The other is to set the ErrorAction parameter to stop. This is supported for cmdlets that accept the so-called common parameters, which Remove-Item does:
Remove-Item "c:\test\new text document.txt" -force -EA "Stop"
To redirect the error stream you use the code 2:
Remove-Item "c:\test\new text document.txt" -Force 2>> $LogFile
That would append the error to the log file. The record isn't written to the error stream if your option is "Stop" however. It is simply included in the exception that is thrown.
Add the ErrorAction switch to your Remove-Item command:
Remove-Item "c:\test\new text document.txt" -force -ErrorAction Stop
There's quite a good treatment on error handling here:
http://blogs.msdn.com/b/kebab/archive/2013/06/09/an-introduction-to-error-handling-in-powershell.aspx
Related
I want to test if New-Item throws an error, for which i am using the -WhatIf parameter and catch the error if an error occurs. If no error occurs, i dont want the -WhatIf output in my console.
I tried the following, but the "WhatIf:"-Output still gets printed to the Powershell console:
New-Item -Path $Path -Name $Name -ItemType $ItemType -ErrorAction stop -WhatIf | Out-Null
I also tried to use $WhatIfPreference as in this StackOverflow answer, which had the same result.
In a script, I use Get-ChildItem -File -Recurse in order to check my permissions on subfolders. Then, I try to read the first character of every files. My goal is to catch Permission Denied type errors using $Error. It works well locally. But when I execute the script on a remote server with a UNC long path, the errors aren't generated. If I run manually the Get-ChildItem command just after the execution of the script, which is supposed to generate some errors, it display the files but does not generate errors. If I wait a few minutes and I run it again, I finally get the errors displayed.
Is there a way to wait for the errors to be generated?
Here is the specific part of my code which doesn't generate any error over the network:
# Check if the current item is a folder or a file
If($elem.Attributes -eq 'Directory')
{
# Get all child items of File type
$subElem = Get-ChildItem -LiteralPath $elem.FullName -File -Recurse -ErrorAction SilentlyContinue
# Parse subfolders and files to check permissions integrity. To generate an Permission Denied error, a file must be open
ForEach($subItem in $subElem)
{
# Read the first character of the current sub-item
Get-Content -LiteralPath $subItem.FullName -Encoding byte -TotalCount 1 -ErrorAction SilentlyContinue | Out-Null
}
}
Else
{
# Read the first character of the current element
Get-Content -LiteralPath $elem.FullName -Encoding byte -TotalCount 1 -ErrorAction SilentlyContinue | Out-Null
}
I finally found the solution myself.
In this script, I use the module NTFSSecurity (https://github.com/raandree/NTFSSecurity) in order to manage ACLs and inheritance. Over the network, it seems to be a bit slow.
Before the bit of code I shared above, I have a few lines which check and updates a bunch of ACLs over the network. As it takes some time, errors are only generated some time after. In this case, if the command encounter an error, it just continues but doesn't display or catch the error at the same time.
I used errors to detect items on which I had to recover the access. Now, I use another cmdlet coming with the NTFSSecurity module, Get-NTFSEffectiveAccess.
I wrote a little function which does perfectly the trick:
Function Check-MyAccess([String]$Path)
{
# Get effective permissions
$effectiveAccess = Get-NTFSEffectiveAccess -Path $Path -ErrorAction SilentlyContinue
# Check to be, at least, able to read the item
If(($effectiveAccess -eq $Null) -or ($effectiveAccess.AccessRights -Match 'Synchronize') -or ((($effectiveAccess.AccessRights -Like '*Read*') -or ($effectiveAccess.AccessRights -Like '*Modify*') -and ($effectiveAccess.AccessControlType -Match 'Deny'))))
{
Return $False
}
Else
{
Return $True
}
}
Below is a simple Try Catch but when I was testing it, when successful it appears to file so I included a location which didnt exist into the variable to test the failure, but oddly even though the failure appears in the ISE window the out still shows as suceeded maing something isnt quite right with below, any ideas as Im lost at what is wrong.
## The location/filename where the Logs will be stored
$varfullpath = "I:\Dev\BI\Reference Data\Release_Event_log.txt"
## The location/filename of the Source to copy from
$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\kljhkjlOS\"
## The location/filename of the Destination to copy to
$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\OkjhkhDS\"
$Server = "CHH-BITEST"
$CurrentDate = Get-Date
try{
Get-ChildItem -Path $sourceDirectory -File -Filter "*.csv" | Copy-Item -Destination $destinationDirectory -ErrorAction Stop
Write-Log -Message "Copy from $sourceDirectory to $destinationDirectory suceeded" -path $varfullpath
}
catch{
$Error[0] | Write-Log -path $varfullpath
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory Failed" -Level Error -path $varfullpath
}
Start-Process notepad $varfullpath ## Opens the file immediately for review
The error is thrown by the get-childitemcommand.
Add the -ErrorAction Stop to this command like that:
Get-ChildItem -Path $sourceDirectory -File -Filter "*.csv" -ErrorAction Stop | Copy-Item -Destination $destinationDirectory -ErrorAction Stop
Some additions to #guiwhatsthat's answer:
PowerShell distinguishes between terminate errors and non-terminating errors. With the help of the common ErrorAction parameter you can setup the behaviour of a cmdlet if such kind of error occurrs. If you want to change the behaviour of multiple cmdlets, or sections in your script you can change the value of $ErrorActionPreference.
From PowerShell documentation:
$ErrorActionPreference
Determines how PowerShell responds to a non-terminating error (an error that does not stop the cmdlet processing) at the command line or in a script, cmdlet, or provider, such as the errors generated by the Write-Error cmdlet.
You can also use the ErrorAction common parameter of a cmdlet to override the preference for a specific command.
Valid values:
Stop: Displays the error message and stops executing.
Inquire: Displays the error message and asks you whether you want to continue.
Continue: Displays the error message and continues (Default) executing.
Suspend: Automatically suspends a workflow job to allow for further investigation. After investigation, the workflow can be resumed.
SilentlyContinue: No effect. The error message is not displayed and execution continues without interruption.
I have a script that includes:
try {
Compress-Archive -Path "$($folder.FullName)\*" -CompressionLevel Optimal -DestinationPath $FullPath -Force
} catch {
Write-Output "`nFailed to create zip"
}
Some of the files in the -Path folder are opened by another user, so Compress-Archive is unable to add them to the zip. It reports this failure to stderr, however it doesn't throw an error.
Is there any way I can determine that the command has only partially succeeded without parsing stderr or re-opening the zip and comparing contents?
It sounds like Compress-Archive is throwing a non-terminating error, which you cannot catch with try / catch.
However, if you add -ErrorAction Stop to the Compress-Archive call, the non-terminating error will be promoted to a script-terminating error, which will trigger your catch handler.
For an overview of PowerShell's complex error-handling rules, see this GitHub issue.
I'm trying to create a script to delete all files out of %temp%. It works but now I need to get rid of the errors. Below is what I have but I'm still getting errors. Any ideas?
try
{
Get-Childitem $Env:temp | Remove-Item -Recurse -Force
}
Catch
{
}
Lots of files in temp can be in use or otherwise inaccessible for removable as that is the scratch space for processes to leave data.
Most common cmdlets support -ErrorAction so that you don't have to the change the default one and risk missing an important error. In your case you can use ...
Get-Childitem $Env:temp | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Since you are not generating terminating errors which is what Try is for you can just remove the block entirely.
FWIW if you really do need try/catch make sure you use terminating errors
try{
Get-ChildItem $Env:temp | Remove-Item -Recurse -Force -ErrorAction Stop
} Catch{
# Handle Stuff
Write-Host "Removal not 100% successful"
}