Powershell terminate execution - powershell

I am using powershell to process csv files in a directory when no file found with current date stamp I want the process to raise an error notifying file not found and exit.
# Powershell raise error and exit
# File name: sale_2020_02_03.csv
$getLatestCSVFile = Get-ChildItem -Path $Folder -Filter "*.csv" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($getLatestCSVFile)
{
try
{
# Process .csv file
}
catch
{
# on error
$ErrorMessage = $_.Exception.Message
throw "$ErrorMessage"
}
}
else
{
# If current date file not found raise error and exit
Send-MailMessage
throw "File not found"
}

As for this...
I would like to have the powershell script to stop execution
... that is what the Exit keyword is for or the '-ErrorAction Stop' option is for.
As for this...
if the code gets into the else block in the else block I want to
notify file not found
... as per my above, same thing, and you have to set that.
Meaning stuff like ...
Clear-Host
$Error.Clear()
try
{
# Statement to try
New-Item -Path 'D:\Temp\DoesNOtExist' -Name 'Test.txt' -ItemType File -ErrorAction Stop
}
catch
{
# What to do with terminating errors
Write-Warning -Message $Error[0]
}
# Results
<#
WARNING: Could not find a part of the path 'D:\Temp\DoesNOtExist\Test.txt'.
#>
Or using multiple catch statements, like ...
Example: Force file warning
Clear-Host
$Error.Clear()
try
{
# Results in NoSupportException
# Statement to try
New-Item -Path 'D:\Temp\Temp' -Name my:Test.txt -ItemType File -ErrorAction Stop
# Results in DirectoryNotFoundException
# New-Item -Path 'D:\Temp\Temp' -Name 'Test.txt' -ItemType File -ErrorAction Stop
}
catch [System.NotSupportedException]
{
# What to do with terminating errors
Write-Warning -Message 'Illegal chracter or filename.'
}
catch [System.IO.DirectoryNotFoundException]
{
# What to do with terminating errors
Write-Warning -Message 'The path is not valid.'
}
catch
{
# What to do with terminating errors
Write-Warning -Message 'An unexpected error occurred.'
}
# Results
<#
WARNING: Illegal character or filename.
#>
Example: Force path warning
Clear-Host
$Error.Clear()
try
{
# Results in NoSupportException
# Statement to try
# New-Item -Path 'D:\Temp\Temp' -Name my:Test.txt -ItemType File -ErrorAction Stop
# Results in DirectoryNotFoundException
New-Item -Path 'D:\Temp\Temp' -Name 'Test.txt' -ItemType File -ErrorAction Stop
}
catch [System.NotSupportedException]
{
# What to do with terminating errors
Write-Warning -Message 'Illegal chracter or filename.'
}
catch [System.IO.DirectoryNotFoundException]
{
# What to do with terminating errors
Write-Warning -Message 'The path is not valid.'
}
catch
{
# What to do with terminating errors
Write-Warning -Message 'An unexpected error occurred.'
}
# Results
<#
WARNING: The path is not valid.
#>
See also this discussion, which is a similar use case. Pay particular attention the the 'Exit vs Return vs Break' answer.
Terminating a script in PowerShell

Related

finally is skipped if the catch is triggered

this is PS 5.1
try {
Send-MailMessage -to $EmailTo -Body $Body -Subject "$TodayDate Report" -From 'r-admin#domain.com' -SmtpServer 'mail-relay' -port '25' -BodyAsHtml -ErrorAction Stop
}
Catch {
Write-Warning "Unable to send email"
& gam user $($EmailTo) sendemail html true to $($EmailTo) subject "$TodayDate Report"
}
finally {
#final cleanup
If (#($UsersResultsArray).count -gt 0) {
remove-Item $UsersResultFileName -Force -ErrorAction SilentlyContinue
}
IF ($ArchiveOverFlowCount -gt 0) {
remove-Item $ArchiveOverFilename -Force -ErrorAction SilentlyContinue
}
If ($ZeroCount -gt 0) {
remove-item $ZeroArrayFileName -Force -ErrorAction SilentlyContinue
}
remove-Item $NumbersTableFilename -Force -ErrorAction SilentlyContinue
}
I have tried many combinations but if the try gets an error the catch works but the finally does not.
I have tried no finally and nothing happens after the catch.
No errors.
I am not sure what is going on or why nothing happens after the catch?
If the Try does not get an error everything works fine.
I tested with a continue in the catch and whatever I do if the catch fires the finally does not.
The documentation says:
The finally keyword is followed by a statement list that runs every time the script is run, even if the try statement ran without error or an error was caught in a catch statement.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-7.2
Now lets test:
try {
get-item C:\nothing.there -ErrorAction:Stop
}
catch {
write-error $_
}
finally {
write-host "finally"
}
#Output:
Write-Error: Cannot find path 'C:\nothing.there' because it does not exist.
finally
So the catch caught the error and finally got executed.
try {
get-item C:\Windows -ErrorAction:Stop
}
catch {
write-error $_
}
finally {
write-host "finally"
}
#Output:
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 12/10/2022 22:29 Windows
finally
Once again finally got executed.
So back to your example, I think that the variables used in the IF statements, e.g. $UsersResultsArray, are empty or the variables that should contain paths like $UsersResultFileName.
To verify if the finally block runs, simply add write-host "finally" to that block and re-run the code, you will see "finally" printed on the screen.
Btw. to verify if the variable holds elements you do not need to count them, this is enough: IF ($UsersResultsArray){}.

Catch Exception from replace method in powershell v5

I am trying to write a script to find and replace a string in a file. How do I catch an exception if the script fails to replace the string for some reason and log it in an external file? Here is what I have so far.
Write-Host "Checking Execution Policy"
$currentExecutionPolicy = Get-ExecutionPolicy
if( $currentExecutionPolicy -eq "RemoteSigned")
{
Write-Host "Execution policy check passed"
}
else
{
"Setting Execution policy to RemoteSigned as per https://msdn.microsoft.com/powershell/reference/5.1/Microsoft.PowerShell.Core/about/about_Execution_Policies"
Set-ExecutionPolicy Remotesigned
}
Write-Host "Starting Script"
#Recurse through all the file shares and find the file.
$rootPath='\\do.main.name\shared\Information Technology\IT\u.name'
$hotspotFile = Get-ChildItem -Path $rootPath -Recurse -Include "hotspot.mac"
Write-Host "Found file" $hotspotFile
$logstring = "Found file" + $hotspotFile
WriteLog $logstring
try
{
(Get-Content $hotspotFile).Replace("olddomain.com","do.main.name") | Set-Content $hotspotFile
}
catch
{
Write-Host "Failed to replace string -" $file
$logstring = "Failed to replace string -" + $file
WriteLog $logstring
}
#Logging
$Logfile = "F:\u.name\Documents\Logs\SCR_To_Find_And_Replace_Old_Domain_String.log"
Function WriteLog
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
Per the comments, I think a failed replace does not generate an exception so you can't use try catch.
Instead you could use an if test, e.g.:
If ($hotspotfile -notcontains "do.main.name") { }
Or test the file for the presence of the old string. You'd probably be wise to also put in an earlier if statement testing if the string was present in the file in the first place and skipping the replace and check if it wasn't.

Catch "Cannot Find File"

I have been searching for a while, but I cannot find the exception in PowerShell that would catch a "Cannot find file" error.
I would also like to have this loop until the user types in the correct file name to get.
# Ask user for file to read from
Try {
$readFile = Read-Host "Name of file to read from: "
$ips = GC $env:USERPROFILE\Desktop\$readFile.txt
}
Catch {
}
The error you get is a non-terminating error, and thus not caught. Add -ErrorAction Stop to your Get-Content statement or set $ErrorActionPreference = 'Stop' and your code will work as you expect:
try {
$readFile = Read-Host "Name of file to read from: "
$ips = GC $env:USERPROFILE\Desktop\$readFile.txt -ErrorAction Stop
} catch {
}
Don't use try/catch blocks for flow control. That is a generally-frowned-on practice, especially in PowerShell, since PowerShell's cmdlets will write errors instead of throwing exceptions. Usually, only non-PowerShell .NET objects will throw exceptions.
Instead, test if the file exists. That gives you much greater error control:
do
{
$readFile = Read-Host "Name of file to read from: "
$path = '{0}\Desktop\{1}.txt' -f $env:USERPROFILE,$readFile
if( (Test-Path -Path $path -PathType Leaf) )
{
break
}
Write-Error -Message ('File ''{0}'' not found.' -f $path)
}
while( $true )

Powershell Try Catch IOException DirectoryExist

I'm using a power shell script to copy some files from my computer to a USB drive. However, even though I'm catching the System.IO exception, I still get the error at the bottom. How do I properly catch this exception, so it shows the message in my Catch block.
CLS
$parentDirectory="C:\Users\someUser"
$userDirectory="someUserDirectory"
$copyDrive="E:"
$folderName="Downloads"
$date = Get-Date
$dateDay=$date.Day
$dateMonth=$date.Month
$dateYear=$date.Year
$folderDate=$dateDay.ToString()+"-"+$dateMonth.ToString()+"-"+$dateYear.ToString();
Try{
New-Item -Path $copyDrive\$folderDate -ItemType directory
Copy-Item $parentDirectory\$userDirectory\$folderName\* $copyDrive\$folderDate
}
Catch [System.IO]
{
WriteOutput "Directory Exists Already"
}
New-Item : Item with specified name E:\16-12-2014 already exists.
At C:\Users\someUser\Desktop\checkexist.ps1:15 char:9
+ New-Item -Path $copyDrive\$folderDate -ItemType directory
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceExists: (E:\16-12-2014:String) [New-Item], IOException
+ FullyQualifiedErrorId : DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand
If you want to catch an exception for a New-Item call, you need to do two things:
Set $ErrorActionPreference = "Stop" by default its value is Continue. This will make the script Stop in case of exceptions.
Trap the correct exception and/or all of them
if you want to trap all exceptions, just use catch without arguments:
catch
{
Write-Output "Directory Exists Already"
}
if you want to trap a specific exception, first find out which one it is, by checking the value of
$error[0].Exception.GetType().FullName
and in your case the value is:
System.IO.IOException
then you can use this value as an argument to your catch, like this:
catch [System.IO.IOException]
{
Write-Output "Directory Exists Already"
}
Source worth reading: An introduction to Error Handling in Powershell
In addition you can spot exactly Directory Exist (unfortunally no File Exist error type for files).
$resExistErr=[System.Management.Automation.ErrorCategory]::ResourceExists
try {
New-Item item -ItemType Directory -ErrorAction Stop
}
catch [System.IO.IOException]
{
if ($_.CategoryInfo.Category -eq $resExistErr) {Write-host "Dir Exist"}
}
If you add -ErrorAction Stop to the line creating the directory you can catch [System.IO.IOException](not [System.IO]). You should also catch all other exceptions that can potentially occur:
try {
New-Item -Path $copyDrive\$folderDate -ItemType directory -ErrorAction Stop
Copy-Item $parentDirectory\$userDirectory\$folderName\* $copyDrive\$folderDate -ErrorAction Stop
}
catch [System.IO.IOException] {
WriteOutput $_.Exception.Message
}
catch {
#some other error
}
I don't suggest actually catching the error in this case. Although that may be the correct action in general, in this specific case I would do the following:
$newFolder = "$copyDrive\$folderDate"
if (-not (Test-Path $newFolder)) {
New-Item -Path $newFolder -ItemType directory
Copy-Item $parentDirectory\$userDirectory\$folderName\* $newFolder
} else {
Write-Output "Directory Exists Already"
}

Windows Script to Rename and move files

I have been tasked with creating an archive process using either a Windows Batch or PowerShell script. I have seen a few examples here on StackExchange but nothing that does exactly what I need and I am running into some issues.
Here is the background:
I have 3 folders
Incoming
Archive
Outgoing
Our main system puts xml files into the Incoming directory and I have to create a script that will run every 5 mins and do the following ....
Iterate through all the xml files in the Incoming folder
Rename them to OriginalFilename.ready_to_archive
Copy all ready_to_archive_files into the Archive directory
Once in the archive directory rename them back to OriginalFilename.xml
Copy all ready_to_archive_files into the Outgoing directory
Once in the Outgoing directory rename them back to OriginalFilename.xml
Delete all ready_to_archive_files from the Incoming directory
Of course if any stage fails then it should not go to the next one since we do not want to delete files that have not been archived properly.
I have had a look at Folder iteration with Move-Item etc but I run into so many issues. This is really not my main working field so any help would be appreciated.
Thanks
-- EDIT --
Here is the PowerShell script I created:
$SCRIPT_DIRECTORY = "d:\Temp\archive\"
$IPS_OUTPUT_DIRECTORY = "d:\Temp\archive\one\"
$ARCHIVE_DIRECTORY = "d:\Temp\archive\two\"
$BIZTALK_INCOMING_DIRECTORY = "d:\Temp\archive\three\"
$ORIGINAL_EXTENSION = ".xml"
$PREP_EXTENSION = ".ready_for_archive"
$ARCHIVE_EXTENSION = ".archive"
#STEP 1
Try
{
"Attempting to rename file from .xml to .archive"
Set-Location -Path $IPS_OUTPUT_DIRECTORY
Get-ChildItem *.xml | Rename-Item -NewName { $_.Name -replace $ORIGINAL_EXTENSION,$PREP_EXTENSION
}
}
Catch [system.exception]
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 1: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop #Stop here and return to the script's
base directory - DO NOT CONTINUE!!!!!
Break
}
Finally
{
"end of step 1"
#STEP 2
Try
{
"Attempting to copy ready_to_archive file from One to Two"
Get-ChildItem -path $IPS_OUTPUT_DIRECTORY -recurse -include *.$PREP_EXTENSION |
Foreach-Object { Copy-Item -path $_ -destination { $_.FullName -replace
$IPS_OUTPUT_DIRECTORY,$ARCHIVE_DIRECTORY}}
}
Catch [system.exception]
{
"caught an error in step 2"
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 2: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop #Stop here and return to the script's base directory - DO NOT CONTINUE!!!!!
Break
}
Finally
{
"end of step 2"
#STEP 3
Try
{
"Attempting to rename files from .ready_to_archive to .archive"
Set-Location -Path $ARCHIVE_DIRECTORY
Get-ChildItem *.xml | Rename-Item -NewName { $_.Name -replace $PREP_EXTENSION,$ARCHIVE_EXTENSION }
}
Catch [system.exception]
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 3: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop #Stop here and return to the script's base directory - DO NOT CONTINUE!!!!!
Break
}
Finally
{
"end of step 3"
#STEP 4
Try
{
"Attempting to copy archive file from one to three"
Copy-Item -path $IPS_OUTPUT_DIRECTORY -include "*.ready_to_archive" -Destination $BIZTALK_INCOMING_DIRECTORY
}
Catch [system.exception]
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 4: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop
Break
}
Finally
{
"end of step 4"
#STEP 5
Try
{
"Attempting to rename file from .ready_to_archive to .xml"
Set-Location -Path $BIZTALK_INCOMING_DIRECTORY
Get-ChildItem *.$PREP_EXTENSION | Rename-Item -NewName { $_.Name -replace $PREP_EXTENSION,$ORIGINAL_EXTENSION }
}
Catch [system.exception]
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 5: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop
Break
}
Finally
{
"end of step 5"
#STEP 6
Try
{
"Attempting to remove original file from One"
Remove-Item $IPS_OUTPUT_DIRECTORY + "*.ready_to_archive" -force #I need to specify the specific file not just any ready_to_archive file!!!!
}
Catch [system.exception]
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
"Error in Step 6: $FailedItem : $ErrorMessage"
Set-Location -Path $SCRIPT_DIRECTORY -ErrorAction Stop
Break
}
Finally
{
"end of step 6"
}
}
}
}
}
"END OF SCRIPT"
}
And here is a screenshot of the execution: (Ok I do not have enough rep points to add a screenshot so here is a txt dump of the output) ...
PS D:\Temp\archive> .\archive.ps1
Attempting to rename file from .xml to .archive
end of step 1
Attempting to copy ready_to_archive file from One to Two
end of step 2
Attempting to rename files from .ready_to_archive to .archive
end of step 3
Attempting to copy archive file from one to three
end of step 4
Attempting to rename file from .ready_to_archive to .xml
end of step 5
Attempting to remove original file from One
Error in Step 6: : A positional parameter cannot be found that accepts argument '+'.
end of step 6
PS D:\Temp\archive>
The only thing that actually works in this script is that the .xml file in folder 'one' is properly renamed to .ready_to_archive' but nothing else happens.
The error is from Remove-Item $IPS_OUTPUT_DIRECTORY + "*.ready_to_archive" -force.
Use this code: Remove-Item -Path "$($IPS_OUTPUT_DIRECTORY)*.ready_to_archive" -force.