PowerShell module terminating unexpectedly - powershell

A PowerShell module I'm working on is behaving very strangely...
I have the following code in a RestoreTestFiles.psm1 file (located in %USERPROFILE%\My Documents\WindowsPowerShell\Modules\RestoreTestFiles\):
Function Restore-TestFiles([string]$backupDir, [string]$destinationDir, [bool]$overwrite)
{
if (!(Test-Path $backupDir))
{
Write-Host "Error, $backupDir does not exist."
return
}
if ($backupDir.EndsWith("\*"))
{
Write-Host "$backupDir ends with \*!"
}
else
{
Write-Host "$backupDir does not end with \*."
}
if ($overwrite)
{
Copy-Item -Path $backupDir -Destination $destinationDir -Recurse -Force
}
else
{
Copy-Item -Path $backupDir -Destination $destinationDir -Recurse
}
Write-Host "Files sucessfully copied from $backupDir to $destinationDir."
}
And I call the function like this:
Import-Module RestoreTestFiles
Restore-TestFiles "C:\some\path\*" "C:\someOther\path\"
For some reason, the output is only either
C:\some\path\* ends with \*!
or
C:\some\path does not end with \*.
But it never runs the Copy-Item or the final Write-Host. If I set a breakpoint, the execution is all messed up:
Stops at breakpoint set on if (!(Test-Path $backupDir))
I step into and it goes to if ($backupDir.EndsWith("\*"))
I step into and it goes to Write-Host "$backupDir ends with \*!"
I step into and it stops running.
Why is the script terminating before it is supposed to?
Edit: I tried moving the function to a regular .ps1 script and called the function from the same script and it works fine...it only terminates prematurely if the function is in a module and called from outside the module. What gives?

I was able to fix this by completely closing all open instances of the PowerShell ISE and re-opening my module.

Related

Copy files from my current directory to new directory

so I am trying to copy 2 files from same folder that my Powershell script is in. I have created script there and also 2 files Moveit1.txt, Moveit2.txt my script is this :
$ScriptDirectory = get-childitem -path $PSScriptRoot
Copy-Item $ScriptDirectory\MoveIt1.txt $env:USERPROFILE + "\AppData\Roaming\" -Force
Copy-Item $ScriptDirectory\MoveIt2.txt "C:\Program Files (x86)\" -Force
But unfortunately it says the files can't be found? But if I check just line $ScriptDirectory it shows where its located and with the files inside. What am I doing wrong?
There is one thing to note:
$ScriptDirectory = Get-ChildItem -Path $PSScriptRoot
$scriptDirectory will most likely contain the 2 MoveIt files in addition to your .ps1 script. When you do:
$ScriptDirectory\MoveIt1.txt
I'm guessing it will end up being something like this when interpreted by Copy-Item:
path\to\script\script.ps1\path\to\script\moveit1.txt\path\to\script\moiveit2.txt\moveit1.txt
Try doing this instead:
Copy-Item (Join-Path $PSScriptRoot MoveIt1.txt) (Join-Path $env:USERPROFILE "\AppData\Roaming\") -Force
Copy-Item (Join-Path $PSScriptRoot MoveIt2.txt) "C:\Program Files (x86)\" -Force
Regarding your Access Denied issue and it working when ran as Administrator with hard-coded paths. You can put this at the top of the script so it elevates itself however this will pop an UAC prompt:
$invocation = "-File `"$PSCommandPath`""
Start-Process powershell -Verb Runas -ArgumentList $invocation
The primary problem with your code is a syntax problem (Santiago's helpful answer addresses additional problems):
In order to pass an expression such as
$env:USERPROFILE + "\AppData\Roaming\"
as a command argument, you need to enclose it in (...).
Neglecting to do so passes three arguments, as the following simplified example demonstrates:
# !! WRONG: Passes *three* arguments instead of the result
# of the intended expression.
PS> Write-Output $env:USERPROFILE + "\AppData\Roaming"
C:\Users\jdoe
+
\App\Data\Roaming
# OK: (...) forces a new parsing context, in which the expression is recognized
# as such.
PS> Write-Output $env:USERPROFILE + "\AppData\Roaming"
C:\Users\jdoe\App\Data\Roaming
As an aside:
You could use $env:APPDATA to directly get the path of interest, and
Even in cases where you do need to build a path from multiple strings, it may be simpler to use an expandable string instead of the + operator: "$env:USERPROFILE\AppData\Roaming" - in this particular case, because the string contains neither spaces nor other special characters (other than the intended $), the double quotes are even optional.
See this answer for more information.
try {
$scriptPath = $PSScriptRoot
if (!$scriptPath)
{
if ($psISE)
{
$scriptPath = Split-Path -Parent -Path $psISE.CurrentFile.FullPath
} else {
Write-Host -ForegroundColor Red "Cannot resolve script file's path"
exit 1
}
}
} catch {
Write-Host -ForegroundColor Red "Caught Exception: $($Error[0].Exception.Message)"
exit 2
}
Write-Host "Path: $scriptPath"
Copy-Item $ScriptPath\onexcuiconcif.xml -Destination "$env:APPDATA\Avaya\Avaya one-x Communicator" -Force
Copy-Item $scriptPath\InstallConfig.xml -Destination "C:\Program Files (x86)\Avaya\Avaya one-X Communicator" -Force

Powershell is missing the terminator: " and Missing closing '}' in statement block or type definition

I am new on powershell and I have an issue. I am trying to make a script that cleans all my directories older than 30 days, but when I run it, it breaks. Does anyone know what problem I have?
$fromNDays = $args[0]
$cutOffDate = (Get-Date).AddDays(-$fromNDays)
$directoriesToDelete = Get-ChildItem -Path $path -Attributes Directory -Filter r* | Where-Object LastWriteTime -le $cutOffDate
echo "deleting from $cutOffDate"
cd $(Agent.ReleaseDirectory)
cd ..\..
pwd
Foreach($directoryToDelete in $directoriesToDelete)
{
if($directoryToDelete.Name -ne "ReleaseRootMapping")
{
try
{
echo "Deleting directory $directoryToDelete"
Remove-Item –path $directoryToDelete.FullName -Force -Recurse
}
catch
{
echo "Failed deleting $directoryToDelete.FullName"
}
}
}
The error that I have when it runs is:
echo "Failed deleting $directoryToDelete.FullName"
+ ~
2020-03-09T09:55:23.3827859Z ##[error]The string is missing the terminator: ".
deleteReleaseDirectoryFrom30.ps1:12 char:5
2020-03-09T09:55:23.3832466Z ##[error]+ {
2020-03-09T09:55:23.3836221Z ##[error]Missing closing '}' in statement block or type definition.
This error apears on line 12, 23, 8 and 10
In your code, the script doesn't know, that $directoryToDelete.FullName is a variable.
So first, put them one into brackets:
try{
echo "Deleting directory ${directoryToDelete}"
Remove-Item –path ${directoryToDelete.FullName} -Force -Recurse
}
catch
{
echo "Failed deleting ${directoryToDelete.FullName}"
}
As described here : https://github.com/PowerShell/vscode-powershell/issues/1308, there is this problem with PS and VSCode.
According to the answers on GitHub, a solution is to paste your code in the Powershell ISE (you can do a simple Windows search for that), and save using it. Then you can continue coding with VsCode or anything else you use.
Hope this helps you, even though I'm a bit late here...

Handle Directory Exists Exception

I am new to PowerShell. I have a piece of code which checks whether a folder "ilalog" exists or not. When I run this script first time, it is checking for the folder "ilalog",if it does not exist,it is creating. When I run the script second time. I receive the error below:
An item with the specified name D:\Temp\ilalog already exists
"FullyQualifiedErrorId :
DirectoryExist,Microsoft.PowerShell.Commands.NewItemCommand".
how to handle this exception
I have tried using try and Catch block
$rgflder="ilalog"
[bool]$checkrg=Test-Path D:\Gdump\$rgfolder -PathType Any
if ( $checkrg -eq $False)
{
try{
New-Item -Path "D:\Gdump" -Name "$rgflder" -ItemType "directory"
}
catch [System.IO.IOException]
{
if ($_.CategoryInfo.Category -eq $resExistErr) {Write-host "Dir Exist"}
}
}
else
{
Write-Output "Directory Exists"
}
If you want to continue processing your script while taking action based on an error type, a simple way is to just examine the $error variable. Using Trap may be an option as well.
$error.clear()
New-Item -Path "D:\Gdump" -Name "$rgflder" -ItemType "directory"
if ($error[0].Exception.GetType().Fullname -eq 'System.IO.IOException') {
"Dir Exists"
}
else {
"Dir was created"
}
If you want to use try-catch, you need to treat your non-terminating error as a terminating error to activate the catch block. You can do this with -ErrorAction Stop.
try {
New-Item -Path "D:\Gdump" -Name "$rgflder" -ItemType "directory" -ErrorAction Stop
}
catch [System.IO.IOException]
{
"Exception caught!"
}
Alternatively, you can manage this within your session by setting $ErrorActionPreference = 'Stop', which will apply to all commands within that session.
Keep in mind that the value passed to -ErrorAction overrides the setting in $ErrorActionPreference. Also, error action settings have no affect on terminating errors. So you cannot expect to set -ErrorAction Continue and have code continue processing on a terminating error.
Many commands return objects that may or may not be terminating errors. You will find more success explicitly, specifying when you want to throw a terminating error.
You can read more about error action preference at About Common Parameters. I actually like Everything About Exceptions for exception handling in PowerShell.
Why not simply do like below?
$rgflder="ilalog"
# combine the path and foldername
$rgPath = Join-Path -Path 'D:\Gdump' -ChildPath $rgflder
# test if the folder already exists
if (Test-Path $rgPath -PathType Container) {
Write-Output "Directory Exists"
}
else {
# if not, create the new folder
try {
$null = New-Item -Path $rgPath -ItemType Directory -ErrorAction Stop
Write-Output "Created directory '$rgPath'"
}
catch {
# something terrible happened..
Write-Error "Error creating folder '$rgPath': $($_.Exception.Message)"
}
}

Bug copying item with Literalpath or intended?

Consider the following really simple code:
Try {
Copy-Item -Path '\\server\g$\thisfolder\thisfiledoesntexist.ini' -Destination 'c:\temp' -ErrorAction Stop
}
Catch {
"Ran into an issue: $_"
}
This will work fine to trap the error for a non-existent souce file. However the following will not - there's no error generated.
Try {
Copy-Item -LiteralPath '\\?\UNC\server\g$\thisfolder\thisfiledoesntexist.ini' -Destination 'c:\temp' -ErrorAction Stop
}
Catch {
"Ran into an issue: $_"
}
However...this WILL trap the error
Try {
Get-ChildItem -LiteralPath '\\?\UNC\server\g$\thisfolder\thisfiledoesntexist.ini' -ErrorAction Stop | Copy-Item
}
Catch {
"Ran into an issue: $_"
}
This is the first time I've had occasion to use Literal paths - is this behaviour intended/expected?
I think you've found a bug with Copy-Item under Windows PowerShell. It seems including a ? character anywhere in input for either -Path or -LiteralPath stops an error from being trapped for a non-existent path.
The bug seems to have been fixed on PowerShell Core, where your code above does result in an exception being caused.

Array handling in powershell

Context :
I have a folder and have some files inside it. I am running a PowerShell script from Jenkins to delete the list of files selected from Jenkins and copy the fresh file from the source. I am trying to delete the files all at an time and copy the list of files like Ctrl+A and copy and paste. I have the script but it is doing individual deletion and copy-paste.
foreach ($database_filename in $database_files) {
Remove-Item -Path $auditFile_Directory -Include $database_filename* -Recurse -Force
Remove-Item -Path $logFile_Directory -Include $database_filename* -Recurse -Force
if ($?) {
log "Deleting the old files complete."
}
try {
log "File sets for $database_filename copying...."
$primary_File = "$database_filename$primaryfile_extn"
$audit_Files = "$database_filename$auditfile_extn"
$log_Files = "$database_filename$logfile_extn"
Copy-Item -Path $sourceFile_Directory$primary_File -Destination $auditFile_Directory
Copy-Item -Path $sourceFile_Directory$audit_Files -Destination $auditFile_Directory
Copy-Item -Path $sourceFile_Directory$log_Files -Destination $logFile_Directory
if ($?) {
log "A fresh golden copy of the db files created."
} else {
Write-Error "Failed! error creating the golden copy, please check the log files"
}
}
catch [System.Net.WebException], [System.IO.IOException]
{
Write-Error "Failed! Unable to copy the SQL files"
}
}
}
You need to split the code into two loops then, the first loop will delete all the files and the second loop will copy all the files.