Powershell Compare string to folders - powershell

I am completely new to Powershell and I want to know if a language code is contained on a folder list. I am using this little script.
set-executionpolicy remotesigned -Scope CurrentUser
$path = "C:\Projects\Eg_Proyect\2022_834"
$lang = "DE-DE"
$Lsta2 = Get-ChildItem -Path $path -Name
$Lsta2
$Lsta2.Count
$Lsta2 -contains $lang
The result is:
0_SOURCE
10_EN-GB_PL
11_EN-GB_CS
12_EN-GB_DE-DE
13_EN-GB_TR
1_EN-GB_ES-ES
2__
3_EN-GB_IT-IT
4_EN-GB_IT-IT
5_EN-GB_HU
6_EN-GB_HU
7_EN-GB_NL-NL
8_EN-GB_NL-NL
9_EN-GB_PT-PT
14
**False**
Why it does not return True?

There is always confusion about the string method .Contains() which looks to find a certain substring withing another string and the PowerShell -contains operator, which looks for a complete, exact (case-insensitive) item in an array.
Since in your case you have an array of strings, you could use:
[bool]($Lsta2 -match 'DE-DE') # uses RegEx. returns True or False because of the cast to `[bool]`
OR
[bool]($Lsta2 -like '*DE-DE*') # uses WildCards ('*' and/or '?')
OR use the .Contains() string operator on every string individually. This is of course a waste of time, AND it works Case-Sensitively, but to show that it can be done:
[bool]($Lsta2 | ForEach-Object { if ($_.Contains('DE-DE')) { $true }})

You can use -match to search with a regex in a string
$Lsta2 -match $lang

Related

How to check if PowerShell result contains these words

I'm doing an IF statement in PowerShell and at some point I do this:
(Get-BitlockerVolume -MountPoint "C:").KeyProtector.keyprotectortype
which gives me the results in this format, on top of each other
I want to write my IF statement to check whether the output of the command above contains both "TpmPin" and "RecoveryPassword" but not sure what the correct syntax is.
I tried something like this but it doesn't work as expected, the result is always true even if it should be false.
if ((Get-BitlockerVolume -MountPoint "C:").KeyProtector.keyprotectortype -contains "tpmpin" && "RecoveryPassword")
this doesn't work either:
if ((Get-BitlockerVolume -MountPoint "C:").KeyProtector.keyprotectortype -contains "tpmpinRecoveryPassword")
p.s I don't want to do nested IF statements because I'm already doing multiple of them.
Make the call to Get-BitLockerVolume before the if statement, store the result in a variable, then use the -and operator to ensure both are found:
$KeyProtectors = Get-BitlockerVolume -MountPoint "C:" |ForEach-Object KeyProtector
if($KeyProtectors.KeyProtectorType -contains 'TpmPin' -and $KeyProtectors.KeyProtectorType -contains 'RecoveryPassword'){
# ... both types were present
}
If you have an arbitrary number of values you want to test the presence of, another way to approach this is to test that none of them are absent:
$KeyProtectors = Get-BitlockerVolume -MountPoint "C:" |ForEach-Object KeyProtector
$mustBePresent = #('TpmPin', 'RecoveryPassword')
if($mustBePresent.Where({$KeyProtectors.KeyProtectorType -notcontains $_}, 'First').Count -eq 0){
# ... all types were present
}
you can write a powershell if check like given below:
if((((Get-BitlockerVolume -MountPoint "C:").KeyProtector.keyprotectortype) -join ",") -eq "Tpm,RecoveryPassword")
{
write-host "matches"
}
else
{
write-host "does not match"
}
matches
Caveat: As told by #MathiasR.Jessen, this solution assumes the order of the values. So, if the values order changes, above solution will not work. We need to follow the solution provided by #MathiasR.Jessen

Array Union functionality [duplicate]

I am using Get-ChildItem to fetch locally installed certs. Then I'd like to delete these certs from my local certificate store. If I execute the script below it will work when there is more than one item returned in both the Get-ChildItem queries. But if only one item is returned in either of the queries the addition of the two collections will fail. Simply because with only one object returned the implementation only returns one instance and not a collection of one. What is the right approach here? I won't know if the result(s) returned from my queries will result in 0, 1 or multiple certs.
$myCerts = Get-ChildItem cert:\CurrentUser\My | Where-Object { $_.Subject -like "CN=$certName" }
$trustedPeopleCerts = Get-ChildItem cert:\CurrentUser\TrustedPeople | Where-Object { $_.Subject -like "CN=$certName" }
$allCerts = $myCerts + $trustedPeopleCerts
foreach ($cert in $allCerts) {
$store = Get-Item $cert.PSParentPath
$store.Open('ReadWrite')
$store.Remove($cert)
$store.Close()
}
If $myCerts might just be a single item, use the array subexpression operator #( ):
$allCerts = #($myCerts) + $trustedPeopleCerts
Another solution that accomplishes the same thing is to use a unary comma operator to ensure that $myCerts is always an array. Consider the following examples that we are using for one element and 0 elements.
$myCerts = Get-Item C:\temp
$myCerts.GetType().FullName
$myCerts = $null
$myCerts.GetType().FullName
The above would generate System.IO.DirectoryInfo and an error for "call[ing] a method on a null-valued expression"
Now lets try the same thing with unary comma operator.
$myCerts = ,(Get-Item C:\temp)
$myCerts = ,($null)
The answer in both cases is System.Object[]
I forgot about one thing this is doing. #() might be preferential in this case as it is an array sub expression. The comma operator I think here is creating a multidimentional array. When used in a pipe it is unrolled and you still get the data you are looking for but it is an important difference to note.
I found a way to do it using a pipeline in case arrays aren't playing nicely. This will write each item to the local pipeline inside the ScriptBlock, which gets captured to the variable.
$merged = & {
$foo
$bar
}

Return boolean from string search

I'm trying to return TRUE from searching Get-ComplianceSearch's output for 'Completed'. My code below is a simple wait loop. But I don't think I'm returning the value correctly because the loop never finishes. I'm fairly new to PowerShell. Please assist or direct.
I'm using Powershell Core 7.1. There are no errors but the Search-String condition never returns TRUE.
try {
$timer = [Diagnostics.Stopwatch]::StartNew()
while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and (-not (Get-ComplianceSearch -
Identity $searchName | Select-String 'Completed' -SimpleMatch -Quiet))) {
Start-Sleep -Seconds $RetryInterval
$totalSecs = [math]::Round($timer.Elapsed.TotalSeconds, 0)
Write-Verbose -Message "Still waiting for action to complete after [$totalSecs]
seconds..."
}
$timer.Stop()
if ($timer.Elapsed.TotalSeconds -gt $Timeout) {
throw 'Action did not complete before timeout period.'
} else {
Write-Verbose -Message 'Action completed before timeout period.'
}
} catch {
Write-Error -Message $_.Exception.Message
}
(This is the expected output of the command Get-ComplianceSearch)
Okay, you don't want to use Select-String here (although you can, see #mklement0's helpful answer, looking at object properties is usually preferred). That is returning an object and you want to check the Status property for "Completed". Make the following change to the -not subexpression:
(-not (Get-ComplianceSearch -Identity $searchName | Where-Object {
$_.Status -eq 'Completed'
}))
The above can be on one line but I broke it up for readability.
Basically, Select-String looks for content in strings. If you are looking for a particular value of an object property however, you can use Where-Object to test for a condition and return any objects matching that condition. In this case, we want to return any object that have a Status of 'Completed', so we can negate that in the if statement.
You (or others) might be wondering how this works since Where-Object returns matching objects, but not booleans. The answer is "truthiness". PowerShell objects are "truthy", which means anything can be evaluated as a [bool].
The following values evaluate to $false in most cases. I've included some gotchas to watch out for when relying on "truthy" values:
A numeric value of 0
A string value of 0 evaluates as $true
Empty arrays
Empty strings
A whitespace-only string or strings consisting only of non-printable characters evaluates as $true
$false
A string value of False evaluates as $true
Most everything else will evaluate to $true. This is also why comparison operators are syntactically optional when checking whether a variable is $null or not. Although there are times when an explicit value check is a good idea as comparison operators compare the actual values instead of only whether the variable "is" or "isn't".
How does this apply to the expression above then? Simple. if statements, always treat the condition expression as a [bool], no conversion required. In addition, logical operators and conditional operators also imply a boolean comparison. For example, $var = $obj assigns $obj to $var, but$var = $obj -eq $obj2 or $var = $obj -and $obj2 will assign $true or $false.
So knowing the above, if Where-Object returns nothing, it's $false. If it returns a tangible object, it's $true.
Bender the Greatest's helpful answer shows a better alternative to using Select-String, because OO-based filtering that queries specific properties is always more robust than searching string representations.
That said, for quick-and-dirty interactive searches, being able to search through a command's formatted display output can be handy, and, unfortunately, Select-String does not do that by default.
As for what you tried:
To make your Select-String work, you need to insert Out-String -Stream before the Select-String call, so as to ensure that the for-display representation is sent through the pipeline, line by line.
# `oss` can be used in lieu of `Out-String -Stream` in PSv5+.
# `sls` can be used in lieu of `Select-String`.
Get-ComplianceSearch | Out-String -Stream | Select-String 'Completed' -SimpleMatch -Quiet
Note:
If you want to search a for-display representation other than the default one, you can insert a Format-* cmdlet call before the Out-String -Stream segment; e.g.
Get-Item / | Format-List * | Out-String -Stream | Select-String ... would search through a list representation of all properties of the object output by Get-Item.
Perhaps surprisingly, Select-String does not search an input object's for-display representation, as you would see it in the console, using the rich formatting provided by PowerShell's display-formatting system.
Instead, it performs simple .ToString() stringification, whose results are often unhelpful and cannot be relied upon to include the values of properties. (E.g.,
#{ foo = 'bar' } | Select-String foo does not work as intended; it is equivalent to
#{ foo = 'bar' }.ToString() | Select-String foo and therefore to
'System.Collections.Hashtable' | Select-String foo
Arguably, Select-String should always have defaulted to searching through the input objects' formatted string representations:
That there is demand for this behavior is evidenced by the fact that PowerShell versions 5 and above (both editions) ship with the oss convenience function, which is a wrapper for Out-String -Stream.
GitHub issue #10726 asks that the current behavior of Select-String be changed to search the for-display string representations by default.

Why am i not able to get result as "true" in below code?

$my_courses = #("course1 # Learning Task Automations # New", "course2 # Advanced Scripting & tool making # New2", "course3 # AD Management using PowerShell /In progress # New3")
$my_courses.Contains("Task")
Your $my_courses is an array of strings, so you should not use the string method .Contains on it as a whole.
You can use that for each array element separately, but then remember .Contains works Case-Sensitively which is probably not what you want:
If what you want is to test whether one of these courses contain the substring "task", and simply return either $true or $false then I suggest one of the following:
$my_courses = "course1 # Learning Task Automations # New",
"course2 # Advanced Scripting & tool making # New2",
"course3 # AD Management using PowerShell /In progress # New3"
use -like with wildcards and cast the result to Boolean. If no element contains "task", the result will be $false
[bool]($my_courses -like "*task*")
use -match and cast the result to Boolean
[bool]($my_courses -match "task")
If you want the word task to be a whole word, enclose it with \b word boundary metacharacters.
This will prevent receiving True on words like "Taskforce"
[bool]($my_courses -match "\btask\b")
use a Where-Object clause to filter the array of elements that contain "task", then again cast to Boolean
[bool]($my_courses | Where-Object { $_ -match 'task' })
use [regex]::IsMatch()
# (?i) makes it case-insensitive and the \b around it make it a whole word search
[regex]::IsMatch($my_courses, '(?i)\btask\b')
The .contains method looks for an exact match e.g.
$my_courses.contains("course1 # Learning Task Automations # New")
The method does not support wildcards - try using -like
$my_courses -like "*Task*"

Is there a way to show all functions in a PowerShell script?

Is there any command to list all functions I've created in a script?
Like i created function doXY and function getABC or something like this.
Then I type in the command and it shows:
Function doXY
Function getABC
Would be a cool feature^^
Thanks for all your help.
You can have PowerShell parse your script, and then locate the function definitions in the resulting Abstract Syntax Tree (AST).
Get-Command is probably the easiest way to access the AST:
# Use Get-Command to parse the script
$myScript = Get-Command .\path\to\script.ps1
$scriptAST = $myScript.ScriptBlock.AST
# Search the AST for function definitions
$functionDefinitions = $scriptAST.FindAll({
$args[0] -is [Management.Automation.Language.FunctionDefinitionAst]
}, $false)
# Report function name and line number in the script
$functionDefinitions |ForEach-Object {
Write-Host "Function '$($_.Name)' found on line $($_.StartLineNumber)!"
}
You can also use this to analyze the functions' contents and parameters if necessary.
Where your script is named things.ps1, something like...
cat ./things.ps1 | grep function
For MacOS/Linux or...
cat ./things.ps1 | select-string function
For Windows.
This is a built-in feature as shown in the PowerShell help files.
About_Providers
Similar questions have been asked before. So, this is a potential duplicate of:
How to get a list of custom Powershell functions?
Answers... Using the PSDrive feature
# To get a list of available functions
Get-ChildItem function:\
# To remove a powershell function
# removes `someFunction`
Remove-Item function:\someFunction
Or
Function Get-MyCommands {
Get-Content -Path $profile | Select-String -Pattern "^function.+" | ForEach-Object {
[Regex]::Matches($_, "^function ([a-z.-]+)","IgnoreCase").Groups[1].Value
} | Where-Object { $_ -ine "prompt" } | Sort-Object
}
Or this one
Get List Of Functions From Script
$currentFunctions = Get-ChildItem function:
# dot source your script to load it to the current runspace
. "C:\someScript.ps1"
$scriptFunctions = Get-ChildItem function: | Where-Object { $currentFunctions -notcontains $_ }
$scriptFunctions | ForEach-Object {
& $_.ScriptBlock
}
As for this...
Thanks, this is kind of what i want, but it also shows functions like
A:, B:, Get-Verb, Clear-Host, ...
That is by design. If you want it another way, then you have to code that.
To get name of functions in any script, it has to be loaded into memory first, then you can dot source the definition and get the internals. If you just want the function names, you can use regex to get them.
Or as simple as this...
Function Show-ScriptFunctions
{
[cmdletbinding()]
[Alias('ssf')]
Param
(
[string]$FullPathToScriptFile
)
(Get-Content -Path $FullPathToScriptFile) |
Select-String -Pattern 'function'
}
ssf -FullPathToScriptFile 'D:\Scripts\Format-NumericRange.ps1'
# Results
<#
function Format-NumericRange
function Flush-NumberBuffer
#>
This function will parse all the functions included in a .ps1 file, and then will return objects for each function found.
The output can be piped directly into Invoke-Expression to load the retuned functions into the current scope.
You can also provide an array of desired names, or a Regular Expression to constrain the results.
My use case was I needed a way for loading individual functions from larger scripts, that I don't own, so I could do pester testing.
Note: only tested in PowerShell 7, but I suspect it will work in older versions too.
function Get-Function {
<#
.SYNOPSIS
Returns a named function from a .ps1 file without executing the file
.DESCRIPTION
This is useful where you have a blended file containing functions and executed instructions.
If neither -Names nor -Regex are provided then all functions in the file are returned.
Returned objects can be piped directly into Invoke-Expression which will place them into the current scope.
Returns an array of objects with the following
- .ToString()
- .Name
- .Parameters
- .Body
- .Extent
- .IsFilter
- .IsWorkFlow
- .Parent
.PARAMETER -Names
Array of Strings; Optional
If provided then function objects of these names will be returned
The name must exactly match the provided value
Case Insensitive.
.PARAMETER -Regex
Regular Expression; Optional
If provided then function objects with names that match will be returned
Case Insensitive
.EXAMPLE
Get all the functions names included in the file
Get-Function -name TestA | select name
.EXAMPLE
Import a function into the current scope
Get-Function -name TestA | Invoke-Expression
#>
param (
$File = "c:\fullpath\SomePowerShellScriptFile.ps1"
,
[alias("Name", "FunctionNames", "Functions")]
$Names
,
[alias("NameRegex")]
$Regex
) # end function
# get the script and parse it
$Script = Get-Command /Users/royomi/Documents/dev/javascript/BenderBot_AI/Import-Function.ps1
$AllFunctions = $Script.ScriptBlock.AST.FindAll({$args[0] -is [Management.Automation.Language.FunctionDefinitionAst]}, $false)
# return all requested functions
$AllFunctions | Where-Object { `
( $Names -and $Names -icontains $_.Name ) `
-or ( $Regex -and $Names -imatch $Regex ) `
-or (-not $Names -and -not $Regex) `
} # end where-object
} # end function Get-Function