When using Test-Path in an if statement, I am looking to get the path that the if statement succeeds with.
For example, these files exist in C:
C:\Test6_1_15.txt
C:\Test6_2_15.txt
C:\Test6_3_15.txt
C:\Test6_4_15.txt
what do I do in the "then" branch?
$Path = "C:\Test6_*_15.txt"
if (Test-Path $Path)
{
# if test passes because there are 4 files that fit the test, but I want to be
# able to output the file that made the if statement succeed.
}
Sounds like you want Resolve-Path:
if(($Paths = #(Resolve-Path "C:\Test6_*_15.txt"))){
foreach($file in $Paths){
# do stuff
}
} else {
# Resolve-Path was unable to resolve "C:\Test6_*_15.txt" to anything
}
You can do get-item $path, that will return actual file name(s) in its result.
You will not get that with Test-Path. Test-Path returns a boolean value(s) representing the presence of the path(s) passed. Looking at the description from TechNet
It returns TRUE ($true) if all elements exist and FALSE ($false) if any are missing
If you just want the actual filenames that match then use Get-Item as it supports standard wildcards. You can get information from the System.IO.FileInfo objects that Get-Item returns.
Related
I'm new to PowerShell and am trying to create a script that goes through a csv file (simple name,value csv) and loads each new line in it as a variable and then runs a function against that set of variables.
I've had success at getting it to work for 1 variable by using the following code snippet:
Import-Csv -Path C:\something\mylist.csv | ForEach-Object {
New-Variable -Name $_.Name -Value $_.Value -Force
}
My csv looks like this:
name,value
RegKey1,"Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\LanmanWorkstation"
Basically it's a list of registry keys each named as RegKey# and then the path of that reg key is the intended value of the variable.
I'm currently playing around with the "Test-Path" cmdlet that just prints out true/false if the passed reg-key exists and then just prints out some text based on if it found the reg key or not.
That snippet looks like so:
Test-Path $RegKey1
IF ($LASTEXITCODE=0) {
Write-Output "It worked"
}
else {
Write-Output "It didn't work"
}
This works fine however what I'm trying to achieve is for powershell to run this cmdlet against each of the lines in the csv file - basically checking each reg key in it and then doing whatever specified to it.
What I'm trying to avoid is declaring hundreds of variables for every regkey I plan on using but instead have this one function that just runs through the csv and every time it runs, it increments the number next to the variable's name - RegKey1,RegKey2,RegKey3 etc.
Let me know if there's a way to do this in powershell or a better way of approaching this altogether. I also apologize in advance if I've not provided enough info, please do let me know.
You need to place your if statement in the Foreach-Object loop. This will also only work, if your variable all get the same name of $RegKey. To incriment, you may use the for loop.
Import-Csv -Path C:\something\mylist.csv | ForEach-Object {
New-Variable -Name $_.Name -Value $_.Value -Force
IF (Test-Path $RegKey1) {
Write-Output "It worked"
}
else {
Write-Output "It didn't work"
}
}
The if statement returns a boolean value of $true, or $false. So theres no need to use $LastExitCode by placing the Test-Path as the condition to evaluate for.
Alternatively, you can use the Foreach loop to accomplish the same thing here:
$CSV = Import-Csv -Path C:\something\mylist.csv
Foreach($Key in $CSV.Value){
$PathTest = Test-Path -Path $Key
if($PathTest) {
Write-Output "It worked"
} else {
Write-Output "It didn't work"
}
}
By iterating(reading through the list 1 at a time) through the csv only selecting the value(Reg Path), we can test against that value by assigning its value to the $PathTest Variable, to be evaluated in your if statement just like above; theres also no need to assign it to a variable and we can just use the Test-Path in your if statement like we did above as well for the same results.
I have two below arrays in powershell
$obj= ('Sales','Finance','config.ini')
$objPath= ('D:\Project','D:\Element','D:\Project')
now $obj can be a folder name or a file name
$objPath is the path where the respective positional $obj will reside
I want to check if that folder or file exist in the respective $objPath
my code:
foreach($element in $obj){
foreach($elementpath in $objPath){
Test-Path $element+'\'+$elementpath
}
}
But it is returning false everytime. Can anyone please suggest where I am doing wrong
I think you've written the statement backwards. When you run it'll check for paths like:
Sales\D:\Project
Sales\D:\Element
Sales\D:\Project
Finance\D:\Project
Finance\D:\Element
Finance\D:\Project
config.ini\D:\Project
config.ini\D:\Element
config.ini\D:\Project
That obviously doesn't look right. You can try a minor re-write like:
Along with reversing the variable references you may want to entertain using Join-Path (as a best practice) like below:
foreach($element in $obj){
foreach($elementpath in $objPath){
Test-Path (Join-Path $elementpath $element)
}
}
It will work with string concatenation like below:
foreach($element in $obj){
foreach($elementpath in $objPath){
Test-Path ($elementpath + '\' + $element)
}
}
Per one of the comments string expansion will also work:
foreach($element in $obj){
foreach($elementpath in $objPath){
Test-Path "$elementpath\$element"
}
}
This is a question of understanding.
I construct a Directory Structure using a string Variable $path. I append the name of the Directory I want to create, this works as expected. When the path is completed I need the complete Directory as System.IO.DirectoryInfo but assigning the Path in this way $Path = Get-Item $Path results in a string type.
$path = "c:\dir1"
If(-Not (Test-Path $path))New-Item -ItemType Directory -Path $path
$path = $path + "\dir2"
If(-Not (Test-Path $path))New-Item -ItemType Directory -Path $path
# Assigned to same variable
$path = Get-Item $path
echo $path.GetType() # = string
# assigned to different variable
$p_a_t_h = Get-Item $path
echo $p_a_t_h.GetType() # = System.IO.DirectoryInfo
# solution but not understood the behavior
[System.IO.DirectoryInfo]$path = Get-Item $path
echo $path.GetType() # = System.IO.DirectoryInfo
It took hours to find out this behavior and I couldn't find any documentation why this is - maybe because I don't know what to search for.
It is clear, that for appending something to a variable, the type of the variable is relevant, but a $path = ... is a "new" assignement and should have the type of the assigned value - at least in my eyes. In the languages I used so far a variable becomes the type of its value and is not converted to a type the variable had earlier or I define the type of a variable and get an error if assigned with wrong type.
Where is the error in my logic?
I think that somewhere in your code you did a left-side cast (on the variable, not the value) to [String], just like you did later in your sample with [System.IO.DirectoryInfo]$path.
The most common way that this happens: Parameters.
Is this taken from a function? Like:
function Invoke-MyThing {
param([String]$Path)
}
Why that matters
When you put the type on the variable, all values assigned to that variable receive that cast.
[String]$Value = 'Hello'
$Value.GetType()
$Value = 3.141
$Value.GetType()
Casting the value only affects that one value:
$V2 = 5
$V2.GetType()
$V2 = [String]9
$V2.GetType()
$V2 = 45
$V2.GetType()
So, remove your previous variable-side cast, or if it's a parameter, just use a different local variable.
Better yet, if it's a parameter, you could make it of type [System.IO.DirectoryInfo] instead.. then it would accept that directly, or even accept a string. You just have to rework your code a little bit to deal with it.
I'm making a function which can take a argument which can be either filesystem or registry path. e.g.
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
'C:\ProgramData\Microsoft\Windows'
I don't want to divide them by named argument but their interfaces aren't compatible. How can I classify them?
You can use this method ($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath) to do that. It have overload, which allows you to extract PowerShell provider and PowerShell drive info from path.
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run',
'C:\ProgramData\Microsoft\Windows' |
ForEach-Object { $Provider = $null } {
[void]$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($_, [ref]$Provider, [ref]$null)
$Provider
}
These commands tell you the types:
(Get-Item 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run').GetType().Name # returns RegistryKey
(Get-Item 'C:\ProgramData\Microsoft\Windows').GetType().Name # returns DirectoryInfo
...or another way of getting the same info...
$item = Get-Item 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'
$item.GetType().Name # returns RegistryKey
$item = Get-Item 'C:\ProgramData\Microsoft\Windows'
$item.GetType().Name # returns DirectoryInfo
In my function i have 3 no mandatory and no positional parameters Folder1, Folder2 and Folder3, before running my function i would like to check for each selected parameter if folder exist. It must be something dynamic because user can specify randomly one or two or all tree folders.
Thanks in advance.
You can pass an array to Test-Path
Test-Path c:, d:, e:
True
True
True
EDIT
So I believe from your comment that you have something like this:
$mypaths = "c:","d:",$null
Test-Path $mypaths
Which issues the following error:
Test-Path : Cannot bind argument to parameter 'Path' because it is null.
Then you can try this:
$mypaths = "c:","d:",$null
$mypaths | Foreach { if ($_){Test-Path $_}}
Which results in only two valid paths.
So you could consider checking the returned values:
$valid = $mypaths | Foreach { if ($_){Test-Path $_}}
write-host "Variable `$mypaths contains $($mypaths.count) paths, and $($valid.count) were found valid"
Which outputs:
Variable $mypaths contains 3 paths, and 2 were found valid