PowerShell Test-Path does not work for argument-parameters - powershell

During my analysis why my script isn't working I could find some solutions that solves some basic parts (also with the help of Stackoverflow), but there is still an issue for which I cannot find a solution.
When I create a temp-directory-variable $PARAM_TEMP and check this with Test-Path everything is working fine (wether the directory exists or not). But on the next part, when I use the same technique for the arguments-parameter $PARAM_DESTINATION the statement-result is wrong (I already found the hint for adding single-quotes - after this, at least the scripts runs through without errors).
When I test my test-output in console in the Windows-Explorer, it finds the directory (thus it definitely exists)
here's the content of my script BeforeInstallationScript2.ps1:
#
# BeforeInstallationScript2.ps1
#
param(
[Parameter(
Mandatory=$true,
Position=0,
HelpMessage='Path to targetDir')]
[string] $PARAM_DESTINATION
)
"1st Argument: $PARAM_DESTINATION"
# create temporary directory if not exists
$PARAM_TEMP=$env:TEMP+"\MyApp"
"temporary Directory: $PARAM_TEMP"
if(!(Test-Path -Path "$PARAM_TEMP" )){
"temp-dir does NOT exist and will be created"
New-Item -ItemType directory -Path $PARAM_TEMP
} else {
"temp-dir exist"
}
# create Timestamp-variable for saving configs
$a = Get-Date
$DATETIME= "" + $a.Year + $a.Month + $a.Day + $a.Hour + $a.Minute
"Timestamp: $DATETIME"
# if there exists already a myApp-Installation, copy config-files
"Parameter-Path=: $PARAM_DESTINATION"
"exists? = " + (Test-Path "'$PARAM_DESTINATION'" )
if((Test-Path -Path "'$PARAM_DESTINATION'" )) {
"param-path exists"
if((Test-Path -Path "'$PARAM_DESTINATION\configuration\MyApp.conf'" )) {
"copy file to $PARAM_TEMP\$DATETIME-MyApp.conf"
Copy-Item "$PARAM_DESTINATION\configuration\MyApp.conf" "$PARAM_TEMP\$DATETIME-MyApp.conf"
}
} else {
"not existing, no files to copy/save"
}
I this script in powershell get the output as follows:
PS X:\MyApp-Setup> C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -Version 2.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -File ".\BeforeInstallationScript2.ps1" "C:\Program Files (x86)\Internet Explorer"
1st Argument: C:\Program Files (x86)\Internet Explorer
temporary Directory: C:\Users\ixmid\AppData\Local\Temp\MyApp
temp-dir does NOT exist and will be created
Verzeichnis: C:\Users\USER\AppData\Local\Temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 16.11.2016 11:01 MyApp
Timestamp: 20161116111
Parameter-Path=: C:\Program Files (x86)\Internet Explorer
exists? = False
not existing, no files to copy/save
PS X:\MyApp-Setup>
As you can see, first Test-Path works fine, and creates the missing directory. But at the second part it doesn't work fine.
Any suggestions, why the second (and third) Test-Path-statements are working wrong?
For completion: second output of executing the script (when MyApp-directory now exists) looks as follows:
1st Argument: C:\Program Files (x86)\Internet Explorer
temporary Directory: C:\Users\USER\AppData\Local\Temp\MyApp
temp-dir exist
Timestamp: 201611161113
Parameter-Path=: C:\Program Files (x86)\Internet Explorer
exists? = False
not existing, no files to copy/save

You use both " quotes and ' quotes. If you want variables, $PARAM_DESTINATION in this case, to expand you need to use double quotes only. So: "$PARAM_DESTINATION".
Read up more on double and single quotes
http://windowsitpro.com/blog/single-quotes-vs-double-quotes-powershell-whats-difference

Related

Remove-item isn't removing non-empty folders [duplicate]

When using the Remove-Item command, even utilizing the -r and -Force parameters, sometimes the following error message is returned:
Remove-Item : Cannot remove item C:\Test Folder\Test Folder\Target: The directory is not empty.
Particularly, this happens when the directory to be removed is opened in Windows Explorer.
Now, while it is possible to avoid this simply by having Windows Explorer closed or not browsing that location, I work my scripts in a multi-user environment where people sometimes just forget to close Windows Explorer windows, I am interested in a solution for deleting entire folders and directories even if they are opened in Windows Explorer.
Is there an option more powerful than -Force that I can set to achieve this?
To reliably reproduce this, create the folder C:\Test Folder\Origin and populate it with some files and subfolders (important), then take the following script or one like it and execute it once. Now open one of the subfolders of C:\Test Folder\Target (in my case, I used C:\Test Folder\Target\Another Subfolder containing A third file.txt), and try running the script again. You will now get the error. If you run the script a third time, you will not get the error again (depending on circumstances that I have yet to determine, though, the error sometimes occurs the second time and then never again, and at other times it occurs every second time).
$SourcePath = "C:\Test Folder\Origin"
$TargetPath = "C:\Test Folder\Target"
if (Test-Path $TargetPath) {
Remove-Item -r $TargetPath -Force
}
New-Item -ItemType directory -Path $TargetPath
Copy-Item $SourcePath -Destination $TargetPath -Force -Recurse -Container
Update: Starting with (at least [1]) Windows 10 version 20H2 (I don't know that Windows Server version and build that corresponds to; run winver.exe to check your version and build), the DeleteFile Windows API function now exhibits synchronous behavior on supported file-systems, including NTFS, which implicitly solves the problems with PowerShell's Remove-Item and .NET's System.IO.File.Delete / System.IO.Directory.Delete (but, curiously, not with cmd.exe's rd /s).
This is ultimately only a timing issue: the last handle to a subdirectory may not be closed yet at the time an attempt is made to the delete the parent directory - and this is a fundamental problem, not restricted to having File Explorer windows open:
Incredibly, the Windows file and directory removal API is asynchronous: that is, by the time the function call returns, it is not guaranteed that removal has completed yet.
Regrettably, Remove-Item fails to account for that - and neither do cmd.exe's rd /s and .NET's [System.IO.Directory]::Delete() - see this answer for details.
This results in intermittent, unpredictable failures.
The workaround comes courtesy of in this YouTube video (starts at 7:35), a PowerShell implementation of which is below:
Synchronous directory-removal function Remove-FileSystemItem:
Important:
The synchronous custom implementation is only required on Windows, because the file-removal system calls on Unix-like platforms are synchronous to begin with. Therefore, the function simply defers to Remove-Item on Unix-like platforms. On Windows, the custom implementation:
requires that the parent directory of a directory being removed be writable for the synchronous custom implementation to work.
is also applied when deleting directories on any network drives.
What will NOT prevent reliable removal:
File Explorer, at least on Windows 10, does not lock directories it displays, so it won't prevent removal.
PowerShell doesn't lock directories either, so having another PowerShell window whose current location is the target directory or one of its subdirectories won't prevent removal (by contrast, cmd.exe does lock - see below).
Files opened with FILE_SHARE_DELETE / [System.IO.FileShare]::Delete (which is rare) in the target directory's subtree also won't prevent removal, though they do live on under a temporary name in the parent directory until the last handle to them is closed.
What WILL prevent removal:
If there's a permissions problem (if ACLs prevent removal), removal is aborted.
If an indefinitely locked file or directory is encountered, removal is aborted. Notably, that includes:
cmd.exe (Command Prompt), unlike PowerShell, does lock the directory that is its current directory, so if you have a cmd.exe window open whose current directory is the target directory or one of its subdirectories, removal will fail.
If an application keeps a file open in the target directory's subtree that was not opened with file-sharing mode FILE_SHARE_DELETE / [System.IO.FileShare]::Delete (using this mode is rare), removal will fail. Note that this only applies to applications that keep files open while working with their content. (e.g., Microsoft Office applications), whereas text editors such as Notepad and Visual Studio Code, by contrast, do not keep they've loaded open.
Hidden files and files with the read-only attribute:
These are quietly removed; in other words: this function invariably behaves like Remove-Item -Force.
Note, however, that in order to target hidden files / directories as input, you must specify them as literal paths, because they won't be found via a wildcard expression.
The reliable custom implementation on Windows comes at the cost of decreased performance.
function Remove-FileSystemItem {
<#
.SYNOPSIS
Removes files or directories reliably and synchronously.
.DESCRIPTION
Removes files and directories, ensuring reliable and synchronous
behavior across all supported platforms.
The syntax is a subset of what Remove-Item supports; notably,
-Include / -Exclude and -Force are NOT supported; -Force is implied.
As with Remove-Item, passing -Recurse is required to avoid a prompt when
deleting a non-empty directory.
IMPORTANT:
* On Unix platforms, this function is merely a wrapper for Remove-Item,
where the latter works reliably and synchronously, but on Windows a
custom implementation must be used to ensure reliable and synchronous
behavior. See https://github.com/PowerShell/PowerShell/issues/8211
* On Windows:
* The *parent directory* of a directory being removed must be
*writable* for the synchronous custom implementation to work.
* The custom implementation is also applied when deleting
directories on *network drives*.
* If an indefinitely *locked* file or directory is encountered, removal is aborted.
By contrast, files opened with FILE_SHARE_DELETE /
[System.IO.FileShare]::Delete on Windows do NOT prevent removal,
though they do live on under a temporary name in the parent directory
until the last handle to them is closed.
* Hidden files and files with the read-only attribute:
* These are *quietly removed*; in other words: this function invariably
behaves like `Remove-Item -Force`.
* Note, however, that in order to target hidden files / directories
as *input*, you must specify them as a *literal* path, because they
won't be found via a wildcard expression.
* The reliable custom implementation on Windows comes at the cost of
decreased performance.
.EXAMPLE
Remove-FileSystemItem C:\tmp -Recurse
Synchronously removes directory C:\tmp and all its content.
#>
[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium', DefaultParameterSetName='Path', PositionalBinding=$false)]
param(
[Parameter(ParameterSetName='Path', Mandatory, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string[]] $Path
,
[Parameter(ParameterSetName='Literalpath', ValueFromPipelineByPropertyName)]
[Alias('PSPath')]
[string[]] $LiteralPath
,
[switch] $Recurse
)
begin {
# !! Workaround for https://github.com/PowerShell/PowerShell/issues/1759
if ($ErrorActionPreference -eq [System.Management.Automation.ActionPreference]::Ignore) { $ErrorActionPreference = 'Ignore'}
$targetPath = ''
$yesToAll = $noToAll = $false
function trimTrailingPathSep([string] $itemPath) {
if ($itemPath[-1] -in '\', '/') {
# Trim the trailing separator, unless the path is a root path such as '/' or 'c:\'
if ($itemPath.Length -gt 1 -and $itemPath -notmatch '^[^:\\/]+:.$') {
$itemPath = $itemPath.Substring(0, $itemPath.Length - 1)
}
}
$itemPath
}
function getTempPathOnSameVolume([string] $itemPath, [string] $tempDir) {
if (-not $tempDir) { $tempDir = [IO.Path]::GetDirectoryName($itemPath) }
[IO.Path]::Combine($tempDir, [IO.Path]::GetRandomFileName())
}
function syncRemoveFile([string] $filePath, [string] $tempDir) {
# Clear the ReadOnly attribute, if present.
if (($attribs = [IO.File]::GetAttributes($filePath)) -band [System.IO.FileAttributes]::ReadOnly) {
[IO.File]::SetAttributes($filePath, $attribs -band -bnot [System.IO.FileAttributes]::ReadOnly)
}
$tempPath = getTempPathOnSameVolume $filePath $tempDir
[IO.File]::Move($filePath, $tempPath)
[IO.File]::Delete($tempPath)
}
function syncRemoveDir([string] $dirPath, [switch] $recursing) {
if (-not $recursing) { $dirPathParent = [IO.Path]::GetDirectoryName($dirPath) }
# Clear the ReadOnly attribute, if present.
# Note: [IO.File]::*Attributes() is also used for *directories*; [IO.Directory] doesn't have attribute-related methods.
if (($attribs = [IO.File]::GetAttributes($dirPath)) -band [System.IO.FileAttributes]::ReadOnly) {
[IO.File]::SetAttributes($dirPath, $attribs -band -bnot [System.IO.FileAttributes]::ReadOnly)
}
# Remove all children synchronously.
$isFirstChild = $true
foreach ($item in [IO.directory]::EnumerateFileSystemEntries($dirPath)) {
if (-not $recursing -and -not $Recurse -and $isFirstChild) { # If -Recurse wasn't specified, prompt for nonempty dirs.
$isFirstChild = $false
# Note: If -Confirm was also passed, this prompt is displayed *in addition*, after the standard $PSCmdlet.ShouldProcess() prompt.
# While Remove-Item also prompts twice in this scenario, it shows the has-children prompt *first*.
if (-not $PSCmdlet.ShouldContinue("The item at '$dirPath' has children and the -Recurse switch was not specified. If you continue, all children will be removed with the item. Are you sure you want to continue?", 'Confirm', ([ref] $yesToAll), ([ref] $noToAll))) { return }
}
$itemPath = [IO.Path]::Combine($dirPath, $item)
([ref] $targetPath).Value = $itemPath
if ([IO.Directory]::Exists($itemPath)) {
syncremoveDir $itemPath -recursing
} else {
syncremoveFile $itemPath $dirPathParent
}
}
# Finally, remove the directory itself synchronously.
([ref] $targetPath).Value = $dirPath
$tempPath = getTempPathOnSameVolume $dirPath $dirPathParent
[IO.Directory]::Move($dirPath, $tempPath)
[IO.Directory]::Delete($tempPath)
}
}
process {
$isLiteral = $PSCmdlet.ParameterSetName -eq 'LiteralPath'
if ($env:OS -ne 'Windows_NT') { # Unix: simply pass through to Remove-Item, which on Unix works reliably and synchronously
Remove-Item #PSBoundParameters
} else { # Windows: use synchronous custom implementation
foreach ($rawPath in ($Path, $LiteralPath)[$isLiteral]) {
# Resolve the paths to full, filesystem-native paths.
try {
# !! Convert-Path does find hidden items via *literal* paths, but not via *wildcards* - and it has no -Force switch (yet)
# !! See https://github.com/PowerShell/PowerShell/issues/6501
$resolvedPaths = if ($isLiteral) { Convert-Path -ErrorAction Stop -LiteralPath $rawPath } else { Convert-Path -ErrorAction Stop -path $rawPath}
} catch {
Write-Error $_ # relay error, but in the name of this function
continue
}
try {
$isDir = $false
foreach ($resolvedPath in $resolvedPaths) {
# -WhatIf and -Confirm support.
if (-not $PSCmdlet.ShouldProcess($resolvedPath)) { continue }
if ($isDir = [IO.Directory]::Exists($resolvedPath)) { # dir.
# !! A trailing '\' or '/' causes directory removal to fail ("in use"), so we trim it first.
syncRemoveDir (trimTrailingPathSep $resolvedPath)
} elseif ([IO.File]::Exists($resolvedPath)) { # file
syncRemoveFile $resolvedPath
} else {
Throw "Not a file-system path or no longer extant: $resolvedPath"
}
}
} catch {
if ($isDir) {
$exc = $_.Exception
if ($exc.InnerException) { $exc = $exc.InnerException }
if ($targetPath -eq $resolvedPath) {
Write-Error "Removal of directory '$resolvedPath' failed: $exc"
} else {
Write-Error "Removal of directory '$resolvedPath' failed, because its content could not be (fully) removed: $targetPath`: $exc"
}
} else {
Write-Error $_ # relay error, but in the name of this function
}
continue
}
}
}
}
}
[1] I've personally verified that the issue is resolved in version 20H2, by running the tests in GitHub issue #27958 for hours without failure; this answer suggests that the problem was resolved as early as version 1909, starting with build 18363.657, but Dinh Tran finds that the issue is not resolved as of build 18363.1316 when removing large directory trees such as node_modules. I couldn't find any official information on the subject.

Win-PS2EXE - Respect the location of the resulting executable

Take these settings for the program Win-PS2EXE:
This is so that the console will show when the exe file is clicked on.
And this code:
$inf_file = "$PSScriptRoot\setup-files\install.inf"
write-host """$inf_file"""
timeout 10
Let us say that the path of the new executable is W:\Apps\Install Scheme.exe
Which means the $inf_file is here W:\Apps\setup-files\install.inf
When I click the converted exe file I get this.
Is there any way to get the correct path of W:\Apps\setup-files\install.inf so that the executable recognises the location of itself when clicked.
I thought that $PSScriptRoot would work.
I'm lost as to how to get around this as the exe file will eventually depend on knowing its location.
Here's the code that can accomplish that.
Function Get-PSScriptPath {
<#
.SYNOPSIS
Returns the current filepath of the .ps1 or compiled .exe with Win-PS2EXE.
.DESCRIPTION
This will return the path of the file. This will work when the .ps1 file is
converted with Win-PS2EXE
.NOTES
Author: Ste
Date Created: 2021.05.03
Tested with PowerShell 5.1 and 7.1.
Posted here: https://stackoverflow.com/q/60121313/8262102
.PARAMETER None
NA
.INPUTS
None. You cannot pipe objects to Get-PSScriptPath.
.OUTPUTS
Returns the current filepath of the .ps1 or compiled .exe with Win-PS2EXE.
.EXAMPLE (When run from a .ps1 file)
PS> Get-PSScriptPath
PS> C:\Users\Desktop\temp.ps1
.EXAMPLE (When run from a compiled .exe file with Win-PS2EXE.
PS> Get-PSScriptPath
PS> C:\Users\Desktop\temp.exe
#>
if ([System.IO.Path]::GetExtension($PSCommandPath) -eq '.ps1') {
$psScriptPath = $PSCommandPath
} else {
# This enables the script to be compiles and get the directory of it.
$psScriptPath = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName
}
return $psScriptPath
}
Get-PSScriptPath
To offer a pragmatic, concise alternative (PSv3+) that always reports the script path as a full path:
One-liner:
$scriptDir = if (-not $PSScriptRoot) { Split-Path -Parent (Convert-Path ([environment]::GetCommandLineArgs()[0])) } else { $PSScriptRoot }
Annotated form:
$scriptDir = if (-not $PSScriptRoot) { # $PSScriptRoot not defined?
# Get the path of the executable *as invoked*, via
# [environment]::GetCommandLineArgs()[0],
# resolve it to a full path with Convert-Path, then get its directory path
Split-Path -Parent (Convert-Path ([environment]::GetCommandLineArgs()[0]))
}
else {
# Use the automatic variable.
$PSScriptRoot
}

Visual studio Build Task Issue for PowerShell inline task - Azure

I am running a vsts build inline PowerShell script task to create package for Azure cloud service. It works fine and create package file from my local machine, but when I try to run from VSTS PowerShell inline task it gives error :
##[error]Cannot find path ‘D:\a_tasks\InlinePowershell_31f040e5-e040-4336-878a-59a493355534\1.1.6\ServiceConfiguration.Cloud.Test.cscfg’ because it does not exist.
Here is my PowerShell inline script below, It fails on the following line:
Copy-Item $serviceConfigurationPath $packageOutDir
I really appreciate your help on this.
Thanks,
# This is the VSTS repository path
$workingDirectory = “$/DevCodeBase/ToolDevBranch1.33”
$webProjectName = “WebRole1”
$cloudProjectName = ‘ProjAzureDeployment’
$evv =’Test’
$cppack = ‘C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe’
$solutionDir = [string]::Format(“{0}”, $workingDirectory)
$webDir = [string]::Format(“{0}\{1}”, $workingDirectory, $webProjectName)
$packageOutDir = [string]::Format(“{0}\{1}”, $workingDirectory, $cloudProjectName)
$rolePropertyFile = [string]::Format(“{0}\{1}\{2}”, $workingDirectory, $cloudProjectName, “roleproperties.txt”)
# Create Role Properties File – This property file specifies the .Net framework against which webrole is going to run.
New-Item $rolePropertyFile -Type file -Force -Value “TargetFrameWorkVersion=v4.5” | Out-Null
New-Item $packageOutDir -Type directory -Force | Out-Null
# CSPack command Definition
$serviceDefinitionPath = [string]::Format(“{0}\{1}\ServiceDefinition.csdef”, $solutionDir, $cloudProjectName)
if ($evv -eq “Test”){
$serviceConfigurationPath = “ServiceConfiguration.Cloud.Test.cscfg”
}
else
{
$serviceConfigurationPath = [string]::Format(“{0}\{1}\ServiceConfiguration.Cloud.cscfg”, $solutionDir, $cloudProjectName)
}
$serviceRole = [string]::Format(“/role:{0};{1}”, $webProjectName, $webDir)
$rolePropertiesFile = [string]::Format(“/rolePropertiesFile:{0};{1}”, $webProjectName, $rolePropertyFile)
$sites = [string]::Format(“/sites:{0};Web;{1}”, $webProjectName, $webDir)
$packageOutput = [string]::Format(“/out:{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
# $packageOutput = [string]::Format(“{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
Write-Host $packageOutput
Write-Host $serviceConfigurationPath
# Build CSPKG file
& “C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe” $serviceDefinitionPath $serviceRole $rolePropertiesFile $sites $packageOutput /useCtpPackageFormat | Out-Null
Write-Host $serviceDefinitionPath
Write-Host $serviceRole
Write-Host $rolePropertiesFile
Write-Host $sites
Write-Host $packageOutput
Write-Host ‘before copy’
# Copy configuration file
Copy-Item $serviceConfigurationPath $packageOutDir
# Remove Role Properties File
Remove-Item -Path $rolePropertyFile -Force | Out-Null
In the VSTS task you'll have to specify an absolute path, otherwise the script will look in the temporary directory created for your inline powershell script.
For instance, you could supply the path to the file as a parameter like
-filepath "$(System.DefaultWorkingDirectory)\Solution\config.json"
(For a list of the variables you can use, have a peek here)
If you want to keep using a relative path, you can move to a file based (ie non-inline) script and use a relative path to that.

How to find whether a certain file is available in the system path?

My Powershell script must be run from a VS command prompt, and I want to verity that by checking that msbuild.exe is in path. I managed to do it using where.exe:
where.exe msbuild.exe > $null
if ($LASTEXITCODE -ne 0)
{
throw "BuildAll must be run from ""Developer Command Prompt for VS2012"" shortcut."
}
However, this doesn't feel like "the Powershell way" - is there a Powershell-native way to do this?
Try this if the msbuild.exe must be in the same folder as the script is
if ( TEST-PATH (JOIN-PATH (Split-Path -parent $MyInvocation.MyCommand.Definition) "msbuild.exe " ))
{
...
}
else
{
...
}
If you want check if build.exe is available as command (any available path from $env:path) you can do:
if ([bool](Get-Command "msbuild.exe" -ea silentlycontinue))
{
...
}

Passing an argument to a powershell script to be used for the Test-Path -include option?

this is my first time asking a question so bear with me. I am teaching myself powershell by writing a few basic maintenance scripts. My question is in regard to a clean up script I am writing which accepts arguments to determine the target directory and files to delete.
The Problem:
The script accepts an optional argument for a list of file extensions to look for when processing the deletion of files. I am trying to test for the existence of the files prior to actually running the delete. I use test-path with the –include parameter to run the check within a ValidateScript block. It works if I pass in a single file extension or no file extensions, however when I try to pass in more than one file extension it fails.
I have tried using the following variations on the code inside the script:
[ValidateScript({ Test-Path $targetDirChk -include $_ })]
[ValidateScript({ Test-Path $targetDirChk -include "$_" })]
[ValidateScript({ Test-Path $targetDirChk -include ‘$_’ })]
For each of the above possibilities I have run the script from the command line using the following variations for the multi extension file list:
& G:\batch\DeleteFilesByDate.ps1 30 G:\log *.log,*.ext
& G:\batch\DeleteFilesByDate.ps1 30 G:\log “*.log, *.ext”
& G:\batch\DeleteFilesByDate.ps1 30 G:\log ‘*.log, *.ext’
Example of the error message:
chkParams : Cannot validate argument on parameter 'includeList'. The " Test-Path $targetDirChk -include "$_" " validation script for the argument with value "*.log, *.ext" did not return true. Determine why the validation script failed and then try the command again.
At G:\batch\DeleteFilesByDate.ps1:81 char:10
+ chkParams <<<< #args
+ CategoryInfo : InvalidData: (:) [chkParams], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,chkParams
The full script is below. I have not yet added the actual code to delete files, because I am still working on accepting and validating the arguments passed in.
I have searched google and stackoverflow but I have not found a solution to this particular problem. I assume I am either doing something wrong with the code, or there is a better way to accomplish what I want to do.
Note:
I should mention that I also tried running the test-path with multiple file extensions outside of the script with no problems:
PS G:\batch\powershell> test-path G:\log\* -include *.log
True
PS G:\batch\powershell> test-path G:\log\* -include *.log, *.ext
True
Script:
# Check that the proper number of arguments have been supplied and if not provide usage statement.
# The first two arguments are required and the third is optional.
if ($args.Length -lt 2 -or $args.Length -gt 3 ){
#Get the name of the script currently executing.
$ScriptName = $MyInvocation.MyCommand.Name
$ScriptInstruction = #"
usage: $ScriptName <Number of Days> <Directory> [File Extensions]
This script deletes files from a given directory based on the file date.
Required Paramaters:
<Number of Days>:
This is an integer representing the number of days worth of files
that should be kept. Anything older than <Number of Days> will be deleted.
<Directory>:
This is the full path to the target folder.
Optional Paramaters:
[File Extensions]
This is the set of file extensions that will be targeted for processing.
If nothing is passed all files will be processed.
"#
write-output $ScriptInstruction
break
}
#Function to validate arguments passed in.
function chkParams()
{
Param(
[Parameter(Mandatory=$true,
HelpMessage="Enter a valid number of days between 1 and 999")]
#Ensure the value passed is between 1 and 999.
#[ValidatePattern({^[1-9][0-9]{0,2}$})]
[ValidateRange(1,999)]
[Int]
$numberOfDays,
[Parameter(Mandatory=$true,
HelpMessage="Enter a valid target directory.")]
#Check that the target directory exists.
[ValidateScript({Test-Path $_ -PathType 'Container'})]
[String]
$targetDirectory,
[Parameter(Mandatory=$false,
HelpMessage="Enter the list of file extensions.")]
#If the parameter is passed, check that files with the passed extension(s) exist.
[ValidateScript({ Test-Path $targetDirChk -include "$_" })]
[String]
$includeList
)
#If no extensions are passed check to see if any files exist in the directory.
if (! $includeList ){
$testResult = Test-path $targetDirChk
if (! $testResult ){
write-output "No files found in $targetDirectory"
exit
}
}
}
#
if ($args[1].EndsWith('\')){
$targetDirChk = $args[1] + '*'
} else {
$targetDirChk = $args[1] + '\*'
}
chkParams #args
-Include on Test-Path is a string[]. You probably want to mirror that definition:
[ValidateScript({ Test-Path $targetDirChk -include $_ })]
[String[]]
$includeList
And drop the "" from there because they will force the argument to be a string and thus trying to match a file that looks like `foo.log blah.ext.
You also have to either put parentheses around that argument when calling the function or remove the space.