Use of where-object - powershell

I would like to use Where-Object to simplify my code and make use of powershell function.
Consider these 2 lists
$list = #('A','B','C')
$list2=#{'A'=1;'B'=2;'E'=3}
What I would like to do is find out in $list all the items that are keys in $list2. I can do this using the normal trivial way.
$r = #()
foreach ($t in $list)
{
$t
if ($list2.ContainsKey($t))
{
$r+=$t
}
}
The code above works but when I issue the command below
$r = $list | Where-Object $list2.Keys.contains($_)
Powershell was not happy and said the method was not supported. I think it should be achievable but I do not know how to issue the proper command. Please help if this is doable as my script seems to be too wordy.

You will need to wrap your the conditional that you are evaluating in your Where-Object in {} to evaluate as a ScriptBlock. Secondly, the contains() method will return a boolean for each member of $list2 so you will want to evaluate that statement for if the returned array contains the value $True.
$r = $list | Where-Object {($list2.Keys.contains($_)) -contains $True}
Alternatively, you could use the .contains() method on the $list2 hashtable itself and avoid getting a value for each key.
$r = $list | Where-Object {$list2.contains($_)}

Related

How could I perform a search using a array file in PowerShell?

I'd like to know how can I perform a concatenation "inside" a powershell array to run a command in windows and get a specific part of code. I produce the code below but isn't works.
[string[]]$a = Get-Content -Path #("C:\users\me\file.txt")
foreach($name in $a){
$b = "Get-Process | net user '$a' /do"
Invoke-Expression $b | Select-String "Nome completo="
}
Thanks for anyone who could help me.
Use $name to refer to the individual array items being iterated ($a refers to the whole array):
foreach($name in $a){
Get-LocalUser $name |ForEach-Object FullName
}

Cannot validate argument on parameter 'Property' when running Where-object on an array of simple strings

I guess I'm overlooking something super obvious here... running this code:
$arraytest = #("one", "two", "three")
$arraytest | Where-Object $_ -Like "*hre*" | foreach { Write-Host $_ }
Produces "Where-Object: Cannot validate argument on parameter 'Property'. The argument is null or empty." in pwsh.
I don't understand why. I checked Microsoft docs for pwsh arrays, and also the docs for Where-Object, but no luck.
Following this question I replaced the line with:
$arraytest | Where {$_ -match "hre"} | foreach { Write-Host $_ }
This works. Is there some hidden default parameter I should have referenced in the -Like example? Why does -match work but -like doesn't?
Since PowerShell 3.0, Where-Object has two distinct syntaxes you can use - but you can't mix them the way you're currently trying.
ScriptBlock syntax (or filter script syntax)
This is the "classic" way of using Where-Object, available since PowerShell 2.0 - you supply a scriptblock and Where-Object then uses this block as a predicate for each input item.
It automatically binds the input values to $_ on each iteration so you can do:
... |Where {$_ -match "hre"}
# or
... |Where {$_ -like "*hre*"}
# or
... |Where {$_.Length -gt 3}
As you can see, we have full access to the input value itself via $_, as well as any properties the object might have.
Property syntax
After the release of PowerShell 2.0, many community members started sharing reusable scripts and snippets online, and it became clear that:
Many people were using the same simple form over and over: {$_.<propertyName> -<operator> <someValue>}
The scriptblock syntax was not immediately intuitive (at least not to anyone who didn't have experience with Perl, from which the { $_ } syntax for anonymous sub routines was largely inspired)
For this reason, Where-Object was augmented with a number of parameter sets in version 3.0 that would allow simplification of the most common use case by providing parameters that look like operators:
Where {$_.Length -match "hre"} could then be written as Where Length -match hre
The only limitation with the new syntax is that you must supply a target property to compare against - so you can no longer compare against the intrinsic value of whatever input value would have been bound to $_ in a filter script.
For this reason, you have to stick with the classic scriptblock-based syntax:
$arraytest = #("one", "two", "three")
$arraytest |Where-Object { $_ -like "*hre*" } |ForEach-Object { Write-Host $_ }
In summary:
Property syntax is useful for writing concise filtering statements when you want to evaluate a specific property value on the input objects
ScriptBlock syntax is required whenever you need to refer to the input object itself via $_
Is there ever a circumstance under which ... | Where-Object $_ -Like "*hre*" is a valid and meaninfful pipeline expression?
Yes!
When you're using Where-Object in a scope where the property name you're filtering on has already been bound to $_:
'Length' |ForEach-Object {
Get-ChildItem -File |Where-Object $_ -gt 1KB
}

Powershell Adjusting Column Values across multiple text files

New to Powershell and looking for some help. I have multiple xyz files that are currently displaying a z value with positive up and I need to make all the values negative (or z-positive down). So far my best attempt has been to try to cobble together what I know from other lines of code, but I'm still far from a solution.
$data = Get-ChildItem "C:\Users\dwilson\Desktop\test" -Recurse |
foreach ($item in $data) {$item.'Col 3' = 0 - $item.'Col 3'}
Any help would be greatly appreciated!
Somewhat of an aside, but you're using mixed syntax here.
foreach ($i in $set) { ... }
This is the Foreach statement. It does not accept input from the pipeline, nor will it automatically send output down the pipeline. See also Get-Help about_Foreach.
Get-ChildItem | ForEach-Object { ... }
This is the ForEach-Object command. It accepts input from the pipeline and sends output down the pipeline. Critically, this command also has a default alias of foreach. See also Get-Help ForEach-Object.
The help for the Foreach statement explains how PowerShell decides what foreach means (emphasis mine):
When Foreach appears in a command pipeline, PowerShell uses the foreach alias, which calls the ForEach-Object command. When you use the foreach alias in a command pipeline, you do not include the ($<item> in $<collection>) syntax as you do with the Foreach statement. This is because the prior command in the pipeline provides this information.
Using the looping over the files returned by Get-ChildItem you can import each with Import-CSV using the -Header parameter to assign the property name for each column. Then we can update the information for that property then export it. Using the ConvertTo-CSV cmdlet and then using Select-Object -Skip 1 we can drop the header off the CSV before exporting it.
$Files = Get-ChildItem "C:\Users\dwilson\Desktop\test" -Recurse
foreach ($File in $Files) {
$Data = Import-CSV $File -Header 'Col1','Col2','Col3'
$newData = ForEach ($Row in $Data) {
$Row.'Col3' = 0 - $Row.'Col3'
$Row
}
$newData |
Convertto-CSV -NoTypeInformation |
Select-Object -Skip 1 |
Out-File "$($File.Fullname).new)"
}

Powershell foreach over two arrays

All,
Trying to get this working but have not had any luck with ps.
$a =
"install.res.1028.dll"
"install.res.1031.dll"
"install.res.1033.dll"
"install.res.1036.dll"
"install.res.1040.dll"
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
$b =
"install.res.1041.dll"
"install.res.1042.dll"
"install.res.2052.dll"
I just wish to have a new array with the values that are found in $a and not found in $b, trying to test it out with write-host but no luck.
I have tried compare-object but I am unable to pull out just the name. I am a totally noob with ps.
Please, any suggestions appreciated.
foreach ($i in $a)
{ foreach-object ($b | where { {$_.name} -ne $i }) { write-host $i}}
Another solution using compare-object as follows:
$c = Compare-Object -ReferenceObject $a -DifferenceObject $b -PassThru
output:
install.res.1028.dll
install.res.1031.dll
install.res.1033.dll
install.res.1036.dll
install.res.1040.dll
CORRECTION:
The above code WILL work for the case where DifferenceObject is guaranteed to be a subset of ReferenceObject. It will FAIL, though, if there are additional objects in DifferenceObject that are not also present in ReferenceObject. The above code returns any objects which are present in EITHER ReferenceObject OR DifferenceObject but NOT in both.
To properly return ONLY objects in ReferenceObject that are not also present in DifferenceObject, the following code is required:
Compare-Object -ReferenceObject $a -DifferenceObject $b |
Where-Object { $_.SideIndicator -eq '<=' } |
ForEach-Object { Write-Output $_.InputObject }
The where-object clause ensures only objects that are present in ReferenceObject are passed down the pipeline.
The foreach-object clause forces the output back to a simple array (ref: Converting Custom Object arrays to String arrays in Powershell - thanks Keith)
$c = $a | Where-Object { $b -notcontains $_ }
This should do the job.
Some Explanation
Where-Object's code block tests each element of the array that's piped into it. The block is supposed to return a boolean value, and if it's true, then the result of the call will contain the item in question.
So for the conditional, we use the -notcontains operator with your second array. $_ refers to the individual item from $a.
This doesn't require an additional or nested loop.

filtering and though a sharepoint list items with powershell

I have tried below but not getting any result back
Not sure if i'm doing this well.
Can i filter in the foreach or in my if statement
Thanks in advance
[DateTime] $CreatedDate = $item["Created"]
$convertedCreatedDate = $CreatedDate.ToString("yyyy-MM-dd")
$today = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
foreach ($item in $list.items | where {$convertedCreatedDate -eq $today}) {
if ($list.items | where {$convertedCreatedDate -eq $today})
{
Write-Host $item["Created"]
}
Write-Host $item["Created"]
}
You can use a complex expression in your foreach as you're doing above. I would wrap it in a #() to make the code a bit more readable and to ensure the result is an array (either length 0, 1 or n) e.g.:
foreach ($item in #($list.items | where {$convertedCreatedDate -eq $today})) {
You can also simplify you're date testing by using the Date property on a DateTime e.g.:
$convertedCreatedDate = ([DateTime]$item["Created"]).Date
$today = (Get-Date).Date
You can also put a complex expression within an if statement condition but PowerShell will only evaluate whether the statement is $true or $false. PowerShell does lots of coercion to try to take something like:
$list.items | where {$convertedCreatedDate -eq $today}
And convert that to a boolean. Essentially if the pipeline evaluates to a non-empty result, the result is $true otherwise it is $false. This is probably not what you intended.
Try this:
$today=(Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
foreach ($item in $list.items) {
[DateTime]$CreatedDate=$item["Created"]
$convertedCreatedDate=$CreatedDate.ToString("yyyy-MM-dd")
if ($convertedCreatedDate -eq $today) {
Write-Host $item["Created"]
}
}