Powershell find value in arraylist - powershell

I hope you can help me out. I work with two ArrayLists:
array1 is filled with log.csv (contains header and logon-data, values of column 'pc' are unique). It's defined as ArrayList because I want to add entries.
#pc,name,date,city
#pc-x,name-1,2017-01-01,berlin
#pc-y,name-1,2017-01-02,berlin
#pc-z,name-2,2017-01-02,munich
[System.Collections.ArrayList]$array1 = Import-Csv log.csv
array2 is filled during runtime
$array2=[System.Collections.ArrayList]#()
... ForEach-Object {
$array2.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate }) >$null }
What I want to do:
Update array1.date=array2.date where array1.pc=array2.pc
If no entry found in array1 I want to add it:
$array1.Add([PSCustomObject]#{ pc=$pcname
name=$loginname
date=$logindate
city='[unknown]' }) >$null
Finally array1 is exported again:
$array1 | Export-Csv log.csv -Encoding UTF8 -NoTypeInformation
So the question is: how can I find the entry in array1 and update it? Trying hard for days now ...

try Something like this:
$array1=import-csv "C:\temp\log.csv"
$array2=import-csv "C:\temp\log2.csv"
#modify founded and output not founded
$toadd=$array2 | %{
$current=$_
$founded=$array1 | where pc -eq $current.pc | %{$_.date=$current.date;$_}
if ($founded -eq $null)
{
$current.city='UNKNOW'
$current
}
}
#output of $array1 modified and elements to add
$array1, $toadd

Here is a sample I created that might help. Note: I use List types instead of ArrayList ones. Also, it assumes only one possible matching PC name in the data to be updated. You'll have to alter it to update the file since it merely updates the first List variable. Let me know how it goes.
[PSCustomObject]
{
[string] $pc,
[string] $name,
[string] $date,
[string] $city
}
[System.Collections.Generic.List[PSCustomObject]] $list1 = Import-Csv "C:\SOSamples\log.csv";
[System.Collections.Generic.List[PSCustomObject]] $list2 = Import-Csv "C:\SOSamples\log2.csv";
[PSCustomObject] $record = $null;
[PSCustomObject] $match = $null;
foreach($record in $list2)
{
# NOTE: This only retrieves the FIRST MATCHING item using a CASE-INSENSITIVE comparison
$match = $list1 | Where { $_.pc.ToLower() -eq $record.pc.ToLower() } | Select -First 1;
if($match -eq $null)
{
Write-Host "Not Found!";
$list1.Add($record);
}
else
{
Write-Host "Found!";
$match.date = $record.date;
}
}
Write-Host "--------------------------------------------------------------------"
foreach($record in $list1)
{
Write-Host $record
}

Related

Powershell - Matching text from array and string?

I have an array that simply pulls a list of numbers in one long column. I am trying to match it with some data in a string and if it matches, I am wanting it to state Down otherwise it will state Up in the output CSV.
Here is my code: `
IF($RESULTS -like $TEST)
{$Output = "DOWN"
}ELSE{
$OUtput = "UP"
}
`
$RESULTS is the array, and $TEST is the string. If I do -match it works, but -match only pulls single digits so it gives false positives. For example, if there is a 3 in the list as well as 638 it will mark them both as down. None of the other switches seem to work like -eq, -like, etc.
What am I missing please?
Thanks much for any assistance!
EDIT:
Sample of Data in $TEST
2
3
5
Sample of Output of $RESULTS
5
628
Since 5 exists in both, my expected output would be DOWN and everything else would be UP.
It sounds like you have two arrays of numbers, and you want to test if the input array contains at least one of the values in the test array.
You can use Compare-Object, which compares two arrays and indicates which elements are different and, with -IncludeEqual, also which ones are the same:
if (
(Compare-Object -IncludeEqual $RESULTS $TEST).
Where({ $_.SideIndicator -eq '==' }).
Count -gt 0
) {
$Output = "DOWN"
}
else {
$Output = "UP"
}
As an aside:
You can use an if statement as an expression, which means you only need to specify $Output once:
$Output =
IF (<# conditional #>) {
"DOWN"
}
ELSE {
"UP"
}
In PowerShell (Core) 7+, you can use ?:, the ternary conditional operator for a more concise solution:
$Output = <# conditional #> ? 'DOWN' : 'UP'
I would do it by using "foreach". Hope this might be helpful
foreach ($result in $RESULTS){
if ($result -like $Test){
$OUTPUT = "Down"}
else{
$OUTPUT= "UP"}
}
In your edited question you show that variable $TEST is also an array, so in that case you can do
$TEST = 2,3,5
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output
In this case, $Output will be DOWN because both arrays have the number 5
If however variable $TEST contains a multiline string, then first create an array out of that like
$TEST = #'
2
3
5
'#
# convert the string to array by splitting at the newline
$TEST = $TEST -split '\r?\n' -ne ''
$RESULTS = 5,628
# compare both arrays for equal items
$Output = if ($TEST | Where-Object { $RESULTS -contains $_ }) {"DOWN"} else {"UP"}
$Output

How to split through the whole list using PowerShell

In my CSV file I have "SharePoint Site" column and a few other columns. I'm trying to split the ID from "SharePoint Site" columns and put it to the new column call "SharePoint ID" but not sure how to do it so I'll be really appreciated If I can get any help or suggestion.
$downloadFile = Import-Csv "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
(($downloadFile -split "/") -split "_") | Select-Object -Index 5
CSV file
SharePoint Site
Include:[https://companyname-my.sharepoint.com/personal/elksn7_nam_corp_kl_com]
Include:[https://companyname-my.sharepoint.com/personal/tzksn_nam_corp_kl_com]
Include:[https://companyname.sharepoint.com/sites/msteams_c578f2/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams%5Fc578f2%2FShared%20Documents%2FBittner%2DWilfong%20%2D%20Litigation%20Hold%2FWork%20History&viewid=b3e993a1%2De0dc%2D4d33%2D8220%2D5dd778853184]
Include:[https://companyname.sharepoint.com/sites/msteams_c578f2/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2Fmsteams%5Fc578f2%2FShared%20Documents%2FBittner%2DWilfong%20%2D%20Litigation%20Hold%2FWork%20History&viewid=b3e993a1%2De0dc%2D4d33%2D8220%2D5dd778853184]
Include:[All]
After spliting this will show it under new Column call "SharePoint ID"
SharePoint ID
2. elksn
3. tzksn
4. msteams_c578f2
5. msteams_c578f2
6. All
Try this:
# Import csv into an array
$Sites = (Import-Csv C:\temp\Modified-Audit-Log-Records.csv).'SharePoint Site'
# Create Export variable
$Export = #()
# ForEach loop that goes through the SharePoint sites one at a time
ForEach($Site in $Sites){
# Clean up the input to leave only the hyperlink
$Site = $Site.replace('Include:[','')
$Site = $Site.replace(']','')
# Split the hyperlink at the fifth slash (Split uses binary, so 0 would be the first slash)
$SiteID = $Site.split('/')[4]
# The 'SharePoint Site' Include:[All] entry will be empty after doing the split, because it has no 4th slash.
# This If statement will detect if the $Site is 'All' and set the $SiteID as that.
if($Site -eq 'All'){
$SiteID = $Site
}
# Create variable to export Site ID
$SiteExport = #()
$SiteExport = [pscustomobject]#{
'SharePoint ID' = $SiteID
}
# Add each SiteExport to the Export array
$Export += $SiteExport
}
# Write out the export
$Export
A concise solution that appends a Sharepoint ID column to the existing columns by way of a calculated property:
Import-Csv 'C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv' |
Select-Object *, #{
Name = 'SharePoint ID'
Expression = {
$tokens = $_.'SharePoint Site' -split '[][/]'
if ($tokens.Count -eq 3) { $tokens[1] } # matches 'Include:[All]'
else { $tokens[5] -replace '_nam_corp_kl_com$' }
}
}
Note:
To see all resulting column values, pipe the above to Format-List.
To re-export the results to a CSV file, pipe to Export-Csv
You have 3 distinct patterns you are trying to extract data from. I believe regex would be an appropriate tool.
If you are wanting the new csv to just have the single ID column.
$file = "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
$IdList = switch -Regex -File ($file){
'Include:.+(?=/(\w+?)_)(?<=personal)' {$matches.1}
'Include:(?=\[(\w+)\])' {$matches.1}
'Include:.+(?=/(\w+?)/)(?<=sites)' {$matches.1}
}
$IdList |
ConvertFrom-Csv -Header "Sharepoint ID" |
Export-Csv -Path $newfile -NoTypeInformation
If you want to add a column to your existing CSV
$file = "C:\AuditLogSearch\New folder\Modified-Audit-Log-Records.csv"
$properties = ‘*’,#{
Name = 'Sharepoint ID'
Expression = {
switch -Regex ($_.'sharepoint Site'){
'Include:.+(?=/(\w+?)_)(?<=personal)' {$matches.1}
'Include:(?=\[(\w+)\])' {$matches.1}
'Include:.+(?=/(\w+?)/)(?<=sites)' {$matches.1}
}
}
}
Import-Csv -Path $file |
Select-Object $properties |
Export-Csv -Path $newfile -NoTypeInformation
Regex details
.+ Match any amount of any character
(?=...) Positive look ahead
(...) Capture group
\w+ Match one or more word characters
? Lazy quantifier
(?<=...) Positive look behind
This would require more testing to see if it works well, but with the input we have it works, the main concept is to use System.Uri to parse the strings. From what I'm seeing, the segment you are looking for is always the third one [2] and depending on the previous segments, perform a split on _ or trim the trailing / or leave the string as is if IsAbsoluteUri is $false.
$csv = Import-Csv path/to/test.csv
$result = foreach($line in $csv)
{
$uri = [uri]($line.'SharePoint Site' -replace '^Include:\[|]$')
$id = switch($uri)
{
{-not $_.IsAbsoluteUri} {
$_
break
}
{ $_.Segments[1] -eq 'personal/' } {
$_.Segments[2].Split('_')[0]
break
}
{ $_.Segments[1] -eq 'sites/' } {
$_.Segments[2].TrimEnd('/')
}
}
[pscustomobject]#{
'SharePoint Site' = $line.'SharePoint Site'
'SharePoint ID' = $id
}
}
$result | Format-List

Powershell Foreach loop in INI file

I am actually new in Powershell scripting with inni files and I need to create a foreach loop that allows me to get multiple variables.
Let me explain :
I have an INI files that contains severals groups (Active Directory groups), I need my powershell script to extract those groups that have names like (group1, group2, group3 and so on).
I tried this piece of code but nothing goes out.
$i=0
$query = $configfile.Domain.group.$i
ForEach($query in $configfile.Domain.group)
{
$i++
write-host $query.$i
}
Looks like i'm pretty bad, any advices ?
The $query in the loop is declaring a new variable which hides the variable declared outside the loop.
Without an example of the ini file, I can only guess, but you could try replacing the loop block with write-host $query.
This will at least show you what is going on.
If your INI file looks anything like this:
[Groups]
group1=example
group2=example1
group3=group1
group4=example3
group5=group2
group6=example5
then you can use the function from here like this:
function Get-IniContent ($filePath) {
$ini = #{}
switch -regex -file $FilePath {
"^\[(.+)\]" # Section
{
$section = $matches[1]
$ini[$section] = #{}
$CommentCount = 0
}
"^(;.*)$" # Comment
{
$value = $matches[1]
$CommentCount = $CommentCount + 1
$name = "Comment" + $CommentCount
$ini[$section][$name] = $value
}
"(.+?)\s*=(.*)" # Key
{
$name,$value = $matches[1..2]
$ini[$section][$name] = $value
}
}
return $ini
}
# the data returned is a Hashtable where each entry is listed
# under a certain section name. This data is UNORDERED by default
$data = Get-IniContent -filePath 'D:\Test\groups.ini'
Get all group names found in the ini file under section 'Groups'
$data['Groups'].Keys | ForEach-Object { $data['Groups'][$_]}
Output:
example
example5
group2
group1
example1
example3
Or get only certain group names that look like 'group' for instance
($data['Groups'].GetEnumerator() | Where-Object { $_.Value -like 'group*' }).Value
# or alternatively do:
# $data['Groups'].Keys | Where-Object { $data['Groups'][$_] -like 'group*' } | ForEach-Object { $data['Groups'][$_] }
Output:
group2
group1
If you want the found group names sorted, append | Sort-Object

Why Isn't This Counting Correctly | PowerShell

Right now, I have a CSV file which contains 3,800+ records. This file contains a list of server names, followed by an abbreviation stating if the server is a Windows server, Linux server, etc. The file also contains comments or documentation, where each line starts with "#", stating it is a comment. What I have so far is as follows.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal = #($arraysplit[0], $arraysplit[1])
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
The goal of this script is to count the total number of Windows servers. My issue is that the first "foreach" block works fine, but the second one results in "$Windows" being 0. I'm honestly not sure why this isn't working. Two example lines of data are as follows:
example:LNX
example2:NT
if the goal is to count the windows servers, why do you need the array?
can't you just say something like
foreach ($thing in $file)
{
if ($thing -notmatch "^#" -and $thing -match "NT") { $windows++ }
}
$arrayfinal = #($arraysplit[0], $arraysplit[1])
This replaces the array for every run.
Changing it to += gave another issue. It simply appended each individual element. I used this post's info to fix it, sort of forcing a 2d array: How to create array of arrays in powershell?.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal += ,$arraysplit
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
1
I also changed the file around and added more instances of both NT and other random garbage. Seems it works fine.
I'd avoid making another ForEach loop for bumping count occurrences. Your $arrayfinal also rewrites everytime, so I used ArrayList.
$file = Get-Content "E:\Code\PS\myPS\2018\Jun\12\allSystems.csv"
$arrayFinal = New-Object System.Collections.ArrayList($null)
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing -split ":"
if($arraysplit[1] -match "NT" -or $arraysplit[1] -match "Windows")
{
$arrayfinal.Add($arraysplit[1]) | Out-Null
}
}
}
Write-Host "Entries with 'NT' or 'Windows' $($arrayFinal.Count)"
I'm not sure if you want to keep 'Example', 'example2'... so I have skipped adding them to arrayfinal, assuming the goal is to count "NT" or "Windows" occurrances
The goal of this script is to count the total number of Windows servers.
I'd suggest the easy way: using cmdlets built for this.
$csv = Get-Content -Path .\file.csv |
Where-Object { -not $_.StartsWith('#') } |
ConvertFrom-Csv
#($csv.servertype).Where({ $_.Equals('NT') }).Count
# Compatibility mode:
# ($csv.servertype | Where-Object { $_.Equals('NT') }).Count
Replace servertype and 'NT' with whatever that header/value is called.

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"]
}
}