Basically I want to do a check if a directory exists then run this section, if not exit.
The script I have is:
$Path = Test-Path c:\temp\First
if ($Path -eq "False")
{
Write-Host "notthere" -ForegroundColor Yellow
}
elseif ($Path -eq "true")
{
Write-Host " what the smokes"
}
But it returns nothing.
The error comes from the fact that the return value of Test-Path is a Boolean type.
Hence, don't compare it to strings representation of Boolean but rather to the actual $false/$true values. Like so,
$Path = Test-Path c:\temp\First
if ($Path -eq $false)
{
Write-Host "notthere" -ForegroundColor Yellow
}
elseif ($Path -eq $true)
{
Write-Host " what the smokes"
}
Also, note that here you could use an else statement here.
Alternatively, you could use the syntax proposed in #user9569124 answer,
$Path = Test-Path c:\temp\First
if (!$Path)
{
Write-Host "notthere" -ForegroundColor Yellow
}
elseif ($Path)
{
Write-Host " what the smokes"
}
In a comparison operation PowerShell automatically converts the second operand to the type of the first operand. Since you're comparing a boolean value to a string, the string will be cast to a boolean value. Empty strings will be cast to $false and non-empty strings will be cast to $true. Jeffrey Snover wrote an article "Boolean Values and Operators" about these automatic conversions that you can check for further details.
As a result this behavior has the (seemingly paradox) effect that each of your comparisons will evaluate to the value of your variable:
PS C:\> $false -eq 'False'
False
PS C:\> $false -eq 'True'
False
PS C:\> $true -eq 'False'
True
PS C:\> $true -eq 'True'
True
Essentially that means that if your Test-Path statements evaluates to $false neither of your conditions will match.
As others have pointed out you can fix the issue by comparing your variable to actual boolean values, or by just using the variable by itself (since it already contains a boolean value that can be evaluated directly). However, you need to be careful with the latter approach. In this case it won't make a difference, but in other situations automatic conversion of different values to the same boolean value might not be the desired behavior. For instance, $null, 0, empty string and empty array are all interpreted as a boolean value $false, but can have quite different semantics depending on the logic in your code.
Also, there is no need to store the result of Test-Path in a variable first. You can put the expression directly into the condition. And since there are only two possible values (a file/folder either exists or doesn't exist), there is no need to compare twice, so your code could be reduced to something like this:
if (Test-Path 'C:\temp\First') {
Write-Host 'what the smokes'
} else {
Write-Host 'notthere' -ForegroundColor Yellow
}
If I'm not mistaken, one can simple say:
if($Path)
OR
if(!$Path)
But I might be wrong as I can't test atm.
Additionally there is the Test-Path cmdlet available. Unfortunately I cannot describe the difference or suggest the most suitable method without knowing the case and scenario.
[EDITED TO CLARIFY ANSWER]
$Path = "C:\"
if($Path)
{
write-host "The path or file exists"
}
else
{
write-host "The path or file isn't there silly bear!"
}
Hope that adds clarity. With this method, no cmdlets needed. The returned boolean is interpreted for you automatically and runs code blocks if it meets the criteria of the test, in this case if the path C:\ exists. This would be true of files in longer file paths, C:\...\...\...\...\file.txt
To make some things clear, always use Test-Path (or Test-Path with Leaf to check for a file).
Examples I've tested:
$File = "c:\path\file.exe"
$IsPath = Test-Path -Path $File -PathType Leaf
# using -Not or ! to check if a file doesn't exist
if (-Not(Test-Path -Path $File -PathType Leaf)) {
Write-Host "1 Not Found!"
}
if (!(Test-Path -Path $File -PathType Leaf)) {
Write-Host "2 Not Found!"
}
# using -Not or ! to check if a file doesn't exist with the result of Test-Path on a file
If (!$IsPath) {
Write-Host "3 Not Found!"
}
If (-Not $IsPath) {
Write-Host "4 Not Found!"
}
# $null checks must be to the left, why not keep same for all?
If ($true -eq $IsPath) {
Write-Host "1 Found!"
}
# Checking if true shorthand method
If ($IsPath) {
Write-Host "2 Found!"
}
if (Test-Path -Path $File -PathType Leaf) {
Write-Host "3 Found!"
}
Related
I want to write a PowerShell script where I will give two string values as parameters, It should check the .txt file and should tell whether the strings are available or not in the given file. For example, if I have a list of employees details. I will give the emp_id and emp_name as input. If the name and id exist in that .txt file it should print that. If not it should print the else statement.
Function Empdetails {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory=$true)]$empid,
[Parameter(Mandatory=$true)]$empname)
$path = Get-Content C:\empdetails.txt | Where-Object {$_ -like '*name*'}
if ($path -eq $true) {
Write-Host "Found"
}
else {
Write-Host "Not Found"
}
}
I tried the above code, But it is working. Could you please help me to figure it out?
You have two parameters but you are not using them in your function, since it's not clear which parameter should be used for the file path and which for the word you're searching for in the file, I have changed the parameter names for something more explanatory.
Also note, the result of below expression will be either an array of strings, a single string or $null:
$path = ... | Where-Object {$_ -like '*name*'}
Hence, your if condition if ($path -eq $true) can never be met unless $path has assigned the literal string True. If, however, you change the order of the condition to $true -eq $path, then the condition can be met and will be $true as long as $path is not $null / empty string.
$content = 'something'
$content -eq $true # => False
$true -eq $content # => True
$content = 'True'
$content -eq $true # => True
$true -eq $content # => True
From equality operators:
The equality operator can compare objects of different types. It is important to understand that the value is on the right-hand side of the comparison can be converted to the type of the left-hand side value for comparison.
Function Empdetails {
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory=$true)]$FilePath,
[Parameter(Mandatory=$true)]$WordToSearch
)
$content = Get-Content $FilePath | Where-Object {$_ -like "*$wordToSearch*"}
if ($content) {
# if `$content` is populated, use return to end the function here
return Write-Host "Found"
}
Write-Host "Not Found"
}
Empdetails -FilePath ./path/to/file.ext -WordToSearch somekeyword
doing some scripting fun with looking for specific file by input.
i want to search specific file by name for example: "abc*" with the prefix either it will be abc.txt, or abc.doc, or what ever...
search recursively in folders, and print if it exists or not.
here is the script i've got so far:
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
Function Get-ItemName {
$Global:ItemName = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a Filename', 'File Name')
}
Get-ItemName
$itemList = Get-ChildItem "\\path_of_folder_with_many_files" -Recurse
$checkItems = 'true'
foreach ($item in $itemList) {
if ((Test-Path -LiteralPath $item) -eq $true -and (Name -like $ItemName)) {
Write-Host "$item - Found" -ForegroundColor Greed
}
elseif ((Test-Path -LiteralPath $item) -eq $false) {
Write-Host "$item - Not Found" -ForegroundColor Red
$checkItems = 'false'
}
}
if ($checkItems -eq $false){
echo ""
Write-Host "Some files was not found, Build Failed" -ForegroundColor Red
exit 1
}
but all it does is printing list of the files and " - Not Found".
and i do have files named "abc.txt" and "abc.doc" and many many more files.
oh yeah and the reason the "find files" script is so long, its because i want to use this powershell script later on a build, and crush it if files doesnt exists.
any ideas whats wrong here?
Either have your function return what was entered in the box and use that or remove that function completely as I have done below:
Add-Type -AssemblyName Microsoft.VisualBasic
$itemName = [Microsoft.VisualBasic.Interaction]::InputBox('Enter a Filename', 'File Name')
if (![string]::IsNullOrWhiteSpace($itemName)) {
# only search for files if there was anything entered in the box
$itemList = #(Get-ChildItem "\\path_of_folder_with_many_files" -Filter "*$itemName*" -File -Recurse)
# now do something with the array of FileInfo objects
# for demo just show how many files were found using the $itemName
Write-Host "Found $($itemList.Count) files with search name '$itemName'"
}
else {
write-host "Nothing was entered in the inputbox.."
}
As you can see, you don't need to use Test-Path, because all files returned by Get-ChildItem of course do exist.
Parameter -Filter here is used with wildcards to search for files with $itemName in their name
Adding switch -File makes sure only files are searched, not folders aswell
Surrounding Get-ChildItem with #() forces the result to be an array, so you can work with the .Count property
I am relatively new to PowerShell and cannot understand why my original attempts failed. I am attempting to validate the bit version of MS Office and perform actions off that. For whatever reason the strings were not comparing properly until I found a solution in the actual question here. Help understanding the difference between the two examples below would be much appreciated.
First attempt:
$getMSBitVersion= Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" | Select-Object -Property Platform
if( $getMSBitVersion -eq "x64" ){
Write-Host "true"
} else {
Write-Host "false"
}
Working solution:
$getMSBitVersion= (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" -Name Platform).Platform
if( $getMSBitVersion -eq "x64" ){
Write-Host "true"
} else {
Write-Host "false"
}
My assumption is the first is outputting an object instead of string and thus the comparison cannot be done. If this is the case, is the working solution the only way/best way to do this?
Thank you Mathias and Abraham.
From what I gather, the following are confirmed methods on how to make the desired comparison.
1: This will scope into the object property of "Platform" by using dot notation and return the string value instead of the whole object.
$getMSBitVersion= (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" -Name Platform).Platform
if( $getMSBitVersion -eq "x64" ){
Write-Host "true"
} else {
Write-Host "false"
}
2: This will take all the properties of the Path and pass through to Select-Object. Select-Object will take and expand the "Property" object and return it as a string.
$getMSBitVersion= Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" | Select-Object -ExpandProperty Platform
if( $getMSBitVersion -eq "x64" ){
Write-Host "true"
} else {
Write-Host "false"
}
I was unable to get this solution to function correctly, but should, in theory, work.
3: This, in theory, should work, but the two objects are recognized differently and do not compare as intended.
$getMSBitVersion= Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" | Select-Object -Property Platform
$test= #{Platform="x64"}
if( Compare-Object $getMSBitVersion $test ){
Write-Host "true"
} else {
Write-Host "false"
}
This began as an effort to have all four (4) of the PowerShell profile scripts identify themselves and their location.
$MyInvocation.ScriptName was suggested for getting the script name, but I have yet to see it be anything other than an empty string. No, not $null, empty (''). This seems counter to many suggestions here on SO.
My first assumption was that $MyInvocation.ScriptName was $null, but that is not the case. To my surprise however, it is considered to be -lt 0. What is the rationale for comparing a String to an Int32?
I did find $MyInvocation.InvocationName which appears to give the script name, but not a directory path to it.
PS C:\Users\lit\Documents\PowerShell> Get-Content .\profile.ps1
Write-Host "Current User, All Hosts # $(Split-Path -Parent $MyInvocation.MyCommand.Definition)\$(Split-Path -Leaf $MyInvocation.MyCommand.Definition)"
Write-Host "`$MyInvocation.ScriptName is $MyInvocation.ScriptName"
Write-Host "`$MyInvocation.ScriptName -eq `$null results in $($MyInvocation.ScriptName -eq $null)"
Write-Host "`$($MyInvocation.ScriptName -eq 0) results in $($MyInvocation.ScriptName -eq 0)"
Write-Host "`$($MyInvocation.ScriptName -lt 0) results in $($MyInvocation.ScriptName -lt 0)"
Write-Host "`$($MyInvocation.ScriptName -eq '') results in $($MyInvocation.ScriptName -eq '')"
Try { '' -eq 0 }
Catch { Write-Host "Caught -lt 0" }
Write-Host $MyInvocation.InvocationName
PS C:\Users\lit\Documents\PowerShell> .\profile.ps1
Current User, All Hosts # C:\Users\lit\Documents\PowerShell\profile.ps1
$MyInvocation.ScriptName is System.Management.Automation.InvocationInfo.ScriptName
$MyInvocation.ScriptName -eq $null results in False
$(System.Management.Automation.InvocationInfo.ScriptName -eq 0) results in False
$(System.Management.Automation.InvocationInfo.ScriptName -lt 0) results in True
$(System.Management.Automation.InvocationInfo.ScriptName -eq '') results in True
False
.\profile.ps1
Suggestion from JosefZ works.
PS C:\src\my-powershell> type .\gname.ps1
Write-Host "`$MyInvocation.InvocationName is $((Resolve-Path $MyInvocation.InvocationName).Path)"
Write-Host (Resolve-Path $MyInvocation.InvocationName).Path
PS C:\src\my-powershell> .\gname.ps1
$MyInvocation.InvocationName is C:\src\my-powershell\gname.ps1
C:\src\my-powershell\gname.ps1
I have tried to collect these possible answers, but still only one or two deliver what is needed. $MyInvocation.Command is always $null and $MyInvocation.ScriptName is always and empty string ('').
PS C:\src\t> Get-Content callme.ps1
"=== These two give the full path and invocation path"
Write-Host "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)\$(Split-Path -Leaf $MyInvocation.MyCommand.Definition)"
Write-Host "`$(`$MyInvocation.InvocationName) is $($MyInvocation.InvocationName)"
"=== `$MyInvocation.Command is `$null, but it, and its Source member, do have a type"
Write-Host "`$MyInvocation.Command -eq `$null is $($MyInvocation.Command -eq $null)"
Write-Host "`$MyInvocation.Command is $MyInvocation.Command"
Write-Host "`$(`$MyInvocation.Command) is $($MyInvocation.Command)"
Write-Host "`$MyInvocation.Command.Source is $MyInvocation.Command.Source"
Write-Host "`$(`$MyInvocation.Command.Source) is $($MyInvocation.Command.Source)"
"==="
Write-Host "`$MyInvocation.ScriptName -eq `$null is $($MyInvocation.ScriptName -eq $null)"
Write-Host "`$MyInvocation.ScriptName -eq '' is $($MyInvocation.ScriptName -eq '')"
Write-Host "`$MyInvocation.ScriptName is $MyInvocation.ScriptName"
Write-Host "`$(`$MyInvocation.ScriptName) is $($MyInvocation.ScriptName)"
PS C:\src\t> .\callme.ps1
=== These two give the full path and invocation path
C:\src\t\callme.ps1
$($MyInvocation.InvocationName) is .\callme.ps1
=== $MyInvocation.Command is $null, but it, and its Source member, do have a type
$MyInvocation.Command -eq $null is True
$MyInvocation.Command is System.Management.Automation.InvocationInfo.Command
$($MyInvocation.Command) is
$MyInvocation.Command.Source is System.Management.Automation.InvocationInfo.Command.Source
$($MyInvocation.Command.Source) is
===
$MyInvocation.ScriptName -eq $null is False
$MyInvocation.ScriptName -eq '' is True
$MyInvocation.ScriptName is System.Management.Automation.InvocationInfo.ScriptName
$($MyInvocation.ScriptName) is
Firstly, you have a bug on line 2 of your script. You probably want to wrap $MyInvocation.ScriptName in $(...):
Write-Host "`$MyInvocation.ScriptName is $($MyInvocation.ScriptName)"
Next, the try{}catch{} will only work if the expression in the try{} block throws an exception. A simple comparison doesn't do that. If you were expecting PowerShell to complain that you are comparing try different types then... it doesn't. See below.
The comment from #PetSerAl is pertinent. When you run the profile from the PowerShell prompt, the ScriptName is what calls the current command. If you were to call your profile script from another script, you'd see the calling script name. I think you might want to look at the $MyInvocation.MyCommand property instead, as per Matthias' suggestion.
And finally...
When you compare a string with a number in the way you are doing, PowerShell "protects" you and automatically treats the second entity (the number) as a string and you're left with a string comparison: is the empty string "" less than the string "0"? Why, yes it is. It is also less than "-9999":
[PS]> "" -lt -9999
True
So -9999 is bigger than nothing? Lets switch them around to check, and we see the same principle at work:
[PS]> -9999 -gt ""
False
Seemingly a contradiction unless you remember that in each case the element on the right-hand-side of the comparison is being treated as a string or int depending on what the left-hand-side is.
In the code below I can use the -and operator to make a compound if statement, but when using Test-Path with -and there is a syntax error.
What is the proper way to use -and with a command like Test-Path?
$a = 1
$b = 1
$c = 1
if ($a -eq $b -and $a -eq $c ) {
write-host "Variables are equal."
}
$path1 = "C:\Windows"
$path2 = "C:\Users"
if (Test-Path $path1 -and Test-Path $path2) {
write-host "paths exist."
}
If you put brackets around your usages of Test-Path then it works, i.e.
$path1 = "C:\Windows"
$path2 = "C:\Users"
if ((Test-Path $path1) -and (Test-Path $path2)) {
write-host "paths exist."
}
DeanOC's helpful answer provides an effective solution.
As for why the parentheses (brackets) are needed:
PowerShell has two fundamental parsing modes:
argument mode, which works like traditional shells
expression mode, which works like traditional programming languages.
Running Get-help about_Parsing provides an introduction to these modes.
Test-Path $path1 and Test-Path $path2 in isolation are parsed in argument mode.
Operator -and can only be used in expression mode.
In order to use the output of the argument-mode Test-Path commands in expression mode, they must be enclosed in (...):
Use of (...) forces a new parsing context, and
the 1st token of a given parsing context determines whether it is parsed in argument or expression mode.
Another thing to consider is PowerShell's pervasive support for collections, which often allows you to operate at a higher level of abstraction:
$paths = "C:\Windows", "C:\Users" # define *array* of input paths
if ((Test-Path $paths) -notcontains $False) { # See if ALL paths exist.
"ALL paths exist."
}
Test-Path accepts an array of paths to test, and outputs a corresponding array of Booleans indicating the respective existence.
Operator -notcontains tests non-membership in the LHS array; in other words: if no Boolean returned by Test-Path is $False, the implication is that all input paths exist.
Note that Write-Host was deliberately omitted, because in order to send something to PowerShell's success stream (the analog to stdout in traditional shells), you don't need an explicit output command at all, and, conversely, Write-Host actually bypasses the success stream - see this blog post.