Each user has a domain1.com and domain2.com separate email address. I would like to compare email addresses within CSV1 & CSV2 and if the prefix name on their emails match, pull the value into CSV1. This will later allow me to run other powershell commands on a source / target kind of scenario.
I have it somewhat, working below although the domain2_email column isn't matching the user row. It looks to be trying to import all of them into an array.
I have used Joe.Bloggs here to test if the IF is working, but ideally - I would like to have it search each entry in CSV1.
End Goal:
Search CSV2 with the name part of the primarySMTPAddress value and if match, put them into another column within CSV1.
CSV1 will have a user with domain1.com and domain2.com values within the row.
# Pull in mailbox data
Get-EXOMailbox -Filter {EmailAddresses -like *#domain1.com -and RecipientTypeDetails -notlike "SharedMailbox"} | Export-Csv -Path './domain1export.csv'
Get-EXOMailbox -Filter {EmailAddresses -like *#domain2.com -and RecipientTypeDetails -notlike "SharedMailbox"} | Export-Csv -Path './domain2xexport.csv'
$outfile = './outfile.csv'
$finalOutfile = './finalOutfile.csv'
# Check if outfile.csv exists, if it does - delete.
if (test-path $outfile) {
Remove-Item $outfile
Write-Host "$outfile has been deleted"
}
$CSV1 = Import-Csv -Path './domain1export.csv'
$CSV2 = Import-Csv -Path './domain2export.csv'
$CVS1 | foreach {
# Splits the name prefix from email
$_ | Select-Object *,#{Name='Name_Prefix';Expression={$_.PrimarySmtpAddress.Split("#")[0] }} | Export-Csv -Path $outfile -Append -NoTypeInformation
}
$outfile2 = Import-Csv -Path $outfile
foreach ($item in $outfile2) {
if ($outfile2.Name_Prefix -match "Joe.Bloggs") {
$item | Select-Object *,#{Name='Domain2_email';Expression={$CSV2.PrimarySmtpAddress}} | Export-Csv -Path $finalOutfile -Append -NoTypeInformation
}
}
Data
CSV1
UserPrincipalName,Alias,DisplayName,EmailAddresses,PrimarySmtpAddress,RecipientType,RecipientTypeDetails
joe.bloggs#domain1.com,d1bloggsj,Domain1 Joe Bloggs,SIP:joe.blogggs#domain1.com SMTP:joe.blogggs#domain1.com,Joe.Bloggs#domain1.com,UserMailbox,UserMailbox
foo.bar#domain1.com,d1barf,Domain1 Foo Bar,SIP:foo.bar#domain1.com SMTP:foo.bar#domain1.com ,foo.bar#domain1.com,UserMailbox,UserMailbox
CSV2
UserPrincipalName,Alias,DisplayName,EmailAddresses,PrimarySmtpAddress,RecipientType,RecipientTypeDetails
joe.bloggs#domain2.com,d2bloggsj,Domain2 Joe Bloggs,SIP:joe.blogggs#domain2.com SMTP:joe.blogggs#domain2.com,Joe.Bloggs#domain2.com,UserMailbox,UserMailbox
foo.bar#domain2.com,d1barf,Domain2 Foo Bar,SIP:foo.bar#domain2.com SMTP:foo.bar#domain2.com ,foo.bar#domain2.com,UserMailbox,UserMailbox
So both CSV1 and CSV2 would contain a column called PrimarySmtpAddress and you need to match those, correct?
Try
$CSV1 = Import-Csv -Path './domain1export.csv'
$CSV2 = Import-Csv -Path './domain2export.csv'
$result = foreach ($item in $csv1) {
$prefix = $item.PrimarySmtpAddress.Split("#")[0]
# try and find a match
$matching = $csv2 | Where-Object { $_.PrimarySmtpAddress.Split("#")[0] -eq $prefix }
$item | Select-Object *, #{Name = 'Name_Prefix'; Expression = {$prefix }},
#{Name = 'Domain2_email';Expression = {$matching.PrimarySmtpAddress }}
}
$finalOutfile = './finalOutfile.csv'
$result | Export-Csv -Path $finalOutfile -NoTypeInformation
BTW. According to the docs, the -Filter parameter is a string, not a scriptblock, so you should use
Get-EXOMailbox -Filter "EmailAddresses -like '*#domain1.com' -and RecipientTypeDetails -ne 'SharedMailbox'"
I would do it like this, first create a hash table using the Csv1 where the Keys are the mail prefix and the Values are the mail suffix and the, using a calculated property with Select-Object you can search for each line on Csv2 if the mail's prefix exists and obtain the corresponding Values (mail suffix):
Note, below code assumes that there are unique mail prefixes, if this was not the case, an if condition would need to be added to the first loop to avoid Key collision. I would also recommend you to use the MailAddress Class to parse your emails.
$map = #{}
foreach($line in $csv1) {
$mail = [mailaddress]$line.PrimarySmtpAddress
$map[$mail.User] = $mail
}
$csv2 | Select-Object *, #{
Name = 'SecondaryDomain'
Expression = { $map[([mailaddress]$_.PrimarySmtpAddress).User].Host }
}, #{
Name = 'SecondarySmtpAddress'
Expression = { $map[([mailaddress]$_.PrimarySmtpAddress).User].Address }
}
$result | Format-Table
Related
so i'm kinda new to PS, I've spent the whole morning trying to figure this out and looking for similar questions/answers here and on Google.
Basically this is my script:
$value = $env:COMPUTERNAME
$csv = Import-Csv -Path '\\UNC\PATH\file.csv'
$props = 'CsSystemFamily','CsDNSHostName', 'CsManufacturer'
Get-ComputerInfo | Select-Object -Property $props | Export-Csv -Path $csv -NoTypeInformation - Delimiter ';' -Append
i'm deploying this as GPO since i need to collect this specific data from some OU's. The thing is i want to check first if the computername exists or not in the CsDNSHostName column so that my script wont add the same computer twice.
Thanks in advance,
I've tried multiple things, but the last thing i found was this:
$value = $env:COMPUTERNAME
if ($file-contains $value) {
write-host 'Computer name already exists'
} else {
Get-ComputerInfo | Select-Object -Property $props | Export-Csv -Path $csv -NoTypeInformation - Delimiter ';' -Append
}
this didn't semm to work since it would just skip if and go straight to else
-contains is the right operator to use, but you must apply it to the array of CsDNSHostName property (column) values, which is easy to do, thanks to member-access enumeration:
$props = 'CsSystemFamily','CsDNSHostName', 'CsManufacturer'
$csvFile = '\\UNC\PATH\file.csv'
$csv = Import-Csv $csvFile -Delimiter ';'
# $csv.CsDNSHostName returns an array of
# all CsDNSHostName column (property) values.
if ($csv.CsDNSHostName -contains $env:COMPUTERNAME) {
Write-Host 'Computer name already exists'
} else {
Get-ComputerInfo |
Select-Object -Property $props |
Export-Csv $csvFile -NoTypeInformation -Delimiter ';' -Append
}
I'm trying (badly) to work through combining CSV files into one file and prepending a column that contains the file name. I'm new to PowerShell, so hopefully someone can help here.
I tried initially to do the well documented approach of using Import-Csv / Export-Csv, but I don't see any options to add columns.
Get-ChildItem -Filter *.csv | Select-Object -ExpandProperty FullName | Import-Csv | Export-Csv CombinedFile.txt -UseQuotes Never -NoTypeInformation -Append
Next I'm trying to loop through the files and append the name, which kind of works, but for some reason this stops after the first row is generated. Since it's not a CSV process, I have to use the switch to skip the first title row of each file.
$getFirstLine = $true
Get-ChildItem -Filter *.csv | Where-Object {$_.Name -NotMatch "Combined.csv"} | foreach {
$filePath = $_
$collection = Get-Content $filePath
foreach($lines in $collection) {
$lines = ($_.Basename + ";" + $lines)
}
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "Combined.csv" $linesToWrite
}
This is where the -PipelineVariable parameter comes in real handy. You can set a variable to represent the current iteration in the pipeline, so you can do things like this:
Get-ChildItem -Filter *.csv -PipelineVariable File | Where-Object {$_.Name -NotMatch "Combined.csv"} | ForEach-Object { Import-Csv $File.FullName } | Select *,#{l='OriginalFile';e={$File.Name}} | Export-Csv Combined.csv -Notypeinfo
Merging your CSVs into one and adding a column for the file's name can be done as follows, using a calculated property on Select-Object:
Get-ChildItem -Filter *.csv | ForEach-Object {
$fileName = $_.Name
Import-Csv $_.FullName | Select-Object #{
Name = 'FileName'
Expression = { $fileName }
}, *
} | Export-Csv path/to/merged.csv -NoTypeInformation
I'm again stuck on something that should be so simple. I have a CSV file in which I need to do a few string modifications and export it back out. The data looks like this:
FullName
--------
\\server\project\AOI
\\server\project\AOI\Folder1
\\server\project\AOI\Folder2
\\server\project\AOI\Folder3\User
I need to do the following:
Remove the "\\server\project" from each line but leave the rest of the line
Delete all rows which do not have a Folder (e.g., in the example above, the first row would be deleted but the other three would remain)
Delete any row with the word "User" in the path
Add a column called T/F with a value of "FALSE" for each record
Here is my initial attempt at this:
Get-Content C:\Folders.csv |
% {$_.replace('\\server\project\','')} |
Where-Object {$_ -match '\\'} |
#Removes User Folders rows from CSV
Where-Object {$_ -notmatch 'User'} |
Out-File C:\Folders-mod.csv
This works to a certain extent, except it deletes my header row and I have not found a way to add a column using Get-Content. For that, I have to use Import-Csv, which is fine, but it seems inefficient to be constantly reloading the same file. So I tried rewriting the above using Import-Csv instead of Get-Content:
$Folders = Import-Csv C:\Folders.csv
foreach ($Folder in $Folders) {
$Folder.FullName = $Folder.FullName.Replace('\\server\AOI\', '') |
Where-Object {$_ -match '\\'} |
Where-Object {$_ -notmatch 'User Files'}
}
$Folders | Export-Csv C:\Folders-mod.csv -NoTypeInformation
I haven't added the coding for adding the new column yet, but this keeps the header. However, I end up with a bunch of empty rows where the Where-Object deletes the line, and the only way I can find to get rid of them is to run the output file through a Get-Content command. This all seems overly complicated for something that should be simple.
So, what am I missing?
Thanks to TheMadTechnician for pointing out what I was doing wrong. Here is my final script (with additional column added):
$Folders= Import-CSV C:\Folders.csv
ForEach ($Folder in $Folders)
{
$Folder.FullName = $Folder.FullName.replace('\\server\project\','')
}
$Folders | Where-Object {$_ -match '\\' -and $_ -notmatch 'User'} |
Select-Object *,#{Name='T/F';Expression={'FALSE'}} |
Export-CSV C:\Folders.csv -NoTypeInformation
I would do this with a Table Array and pscustomobject.
#Create an empty Array
$Table = #()
#Manipulate the data
$Fullname = Get-Content C:\Folders.csv |
ForEach-Object {$_.replace('\\server\project\', '')} |
Where-Object {$_ -match '\\'} |
#Removes User Folders rows from CSV
Where-Object {$_ -notmatch 'User'}
#Define custom objects
Foreach ($name in $Fullname) {
$Table += [pscustomobject]#{'Fullname' = $name; 'T/F' = 'FALSE'}
}
#Export results to new csv
$Table | Export-CSV C:\Folders-mod.csv -NoTypeInformation
here's yet another way to do it ... [grin]
$FileList = #'
FullName
\\server\project\AOI
\\server\project\AOI\Folder1
\\server\project\AOI\Folder2
\\server\project\AOI\Folder3\User
'# | ConvertFrom-Csv
$ThingToRemove = '\\server\project'
$FileList |
Where-Object {
# toss out any blank lines
$_ -and
# toss out any lines with "user" in them
$_ -notmatch 'User'
} |
ForEach-Object {
[PSCustomObject]#{
FullName = $_.FullName -replace [regex]::Escape($ThingToRemove)
'T/F' = $False
}
}
output ...
FullName T/F
-------- ---
\AOI False
\AOI\Folder1 False
\AOI\Folder2 False
notes ...
putting a slash in the property name is ... icky [grin]
that requires wrapping the property name in quotes every time you need to access it. try another name - perhaps "Correct".
you can test for blank array items [lines] with $_ all on its own
the [regex]::Escape() stuff is really quite handy
I've figured out how to compare single columns in two files, but I cant figure out how to compare two files, with one column in the first and multiple columns in the second file. Both containing emails.
First file.csv (contains single column with emails)
john#email.com
jack#email.com
jill#email.com
Second file.csv (contains multiple column with emails)
john#email.nl,john#email.eu,john#email.com
jill#email.se,jill#email.com,jill#email.us
By comparing I would like to output, the difference. This would result in.
Output.csv
jack#email.com
Anyone able to help me? :)
Single columns comparison and output difference
#Line extracts emails from list
$SubscribedMails = import-csv .\subscribed.csv | Select-Object -Property email
#Line extracts emails from list
$ValidEmails = import-csv .\users-emails.csv | Select-Object -Property email
$compare = Compare-Object $SubscribedMails $ValidEmails -property email -IncludeEqual | where-object {$_.SideIndicator -eq "<="} | Export-csv .\nonvalid-emails.csv –NoTypeInformation
(Get-Content .\nonvalid-emails.csv) | ForEach-Object { $_ -replace ',"<="' } > .\nonvalid-emails.csv
Since the first file already contains email addresses per column, you can import it right away.
Take the second file and split the strings containing several addresses.
A new array with seperate addresses will be generated.
Judging from your output, you only seek addresses that are within the first csv but not in the second.
Your code could look like this:
$firstFile = Get-Content 'FirstFile.csv'
$secondFile = (Get-Content 'SecondFile.csv').Split(',')
foreach ($item in $firstFile) {
if ($item -notin $secondFile) {
$item | Export-Csv output.csv -Append -NoTypeInformation
}
}
If you want to maintain your code, can you consider a script like:
#Line extracts emails from list
$SubscribedMails = import-csv .\subscribed.csv | Select-Object -Property email
Rename-Item .\users-emails.csv users-emails.csv.bk
Get-Content .\users-emails.csv.bk).replace(',', "`r`n") | Set-Content .\users-emails.csv
#Line extracts emails from list
$ValidEmails = import-csv .\users-emails.csv | Select-Object -Property email
$compare = Compare-Object $SubscribedMails $ValidEmails -property email -IncludeEqual | where-object {$_.SideIndicator -eq "<="} | Export-csv .\nonvalid-emails.csv –NoTypeInformation
(Get-Content .\nonvalid-emails.csv) | ForEach-Object { $_ -replace ',"<="' } > .\nonvalid-emails.csv
Remove-Item .\users-emails.csv
Rename-Item .\users-emails.csv.bk users-emails.csv
or, more simplest
#Line extracts emails from list
$SubscribedMails = import-csv .\subscribed.csv | Select-Object -Property email
Get-Content .\users-emails.csv).replace(',', "`r`n") | Set-Content .\users-emails.csv.bk
#Line extracts emails from list
$ValidEmails = import-csv .\users-emails.csv.bk | Select-Object -Property email
$compare = Compare-Object $SubscribedMails $ValidEmails -property email -IncludeEqual | where-object {$_.SideIndicator -eq "<="} | Export-csv .\nonvalid-emails.csv –NoTypeInformation
(Get-Content .\nonvalid-emails.csv) | ForEach-Object { $_ -replace ',"<="' } > .\nonvalid-emails.csv
Remove-Item .\users-emails.csv.bk
None of the suggestions so far works :(
Still hoping :)
Will delete comment when happy :p
Can you try this?
$One = (Get-Content .\FirstFile.csv).Split(',')
$Two = (Get-Content .\SecondFile.csv).Split(',')
$CsvPath = '.\Output.csv'
$Diff = #()
(Compare-Object ($One | Sort-Object) ($two | Sort-Object)| `
Where-Object {$_.SideIndicator -eq '<='}).inputobject | `
ForEach-Object {$Diff += New-Object PSObject -Property #{email=$_}}
$Diff | Export-Csv -Path $CsvPath -NoTypeInformation
Output.csv will contain entries that exist in FirstFile but not SecondFIle.
I need to take a slew of csv files from a directory and get them into an array in Powershell (to eventually manipulate and write back to a CSV).
The problem is there are 5 file types. I need around 8 columns from each. The columns are essentially the same, but have different headings.
Is there an easy way to do this? I started creating a custom object with my 8 fields, looping through the files importing each one, looking at the filename (which tells me the column names I need) and then a bunch of ifs to add it to my custom object array.
I was wondering if there is a simpler way...like with a template saying which columns from each file.
wound up doing this. It may have not been the most efficient, but works. I wound up writing out each file separately and combining at the end as PS really got bogged down (over a million rows combined).
$Newcsv = #()
$path = "c:\scrap\BWFILES\"
$files = gci -path $path -recurse -filter *.csv | Where-Object { ! ($_.psiscontainer) }
$counter=1
foreach($file in $files)
{
$csv = Import-Csv $file.FullName
if ($file.Name -like '*SAV*')
{
$Newcsv = $csv | Select-Object #{Name="PRODUCT";Expression={"SV"}},DMBRCH,DMACCT,DMSHRT
}
if ($file.Name -like '*TIME*')
{
$Newcsv = $csv | Select-Object #{Name="PRODUCT";Expression={"TM"}},TMBRCH,TMACCT,TMSHRT
}
if ($file.Name -like '*TRAN*')
{
$Newcsv = $csv | Select-Object #{Name="PRODUCT";Expression={"TR"}},DMBRCH,DMACCT,DMSHRT
}
if ($file.Name -like '*LN*')
{
$Newcsv = $csv | Select-Object #{Name="PRODUCT";Expression={"LN"}},LNBRCH,LNNOTE,LNSHRT
}
$Newcsv | Export-Csv "C:\scrap\$file.name$counter.csv" -force -notypeinformation
$counter++
}
get-childItem "c:\scrap\*.csv" | foreach {
$filePath = $_
$lines = $lines = Get-Content $filePath
$linesToWrite = switch($getFirstLine) {
$true {$lines}
$false {$lines | Select -Skip 1}
}
$getFirstLine = $false
Add-Content "c:\scrap\combined.csv" $linesToWrite
}
With a hashtable for reference, a little RegEx matching, and using the automatic variable $Matches in a ForEach-Object loop (alias % used) that could all be shortened to:
$path = "c:\scrap\BWFILES\"
$Reference = #{
'SAV' = 'SV'
'TIME' = 'TM'
'TRAN' = 'TR'
'LN'='LN'
}
Set-Content -Value "PRODUCT,BRCH,ACCT,SHRT" -Path 'c:\scrap\combined.csv'
gci -path $path -recurse -filter *.csv | Where-Object { !($_.psiscontainer) -and $_.Name -match ".*(SAV|TIME|TRAN|LN).*"}|%{
$Product = $Reference[($Matches[1])]
Import-CSV $_.FullName | Select-Object #{Name="PRODUCT";Expression={$Product}},*BRCH,#{l='Acct';e={$_.LNNOTE, $_.DMACCT, $_.TMACCT|?{$_}}},*SHRT | ConvertTo-Csv -NoTypeInformation | Select -Skip 1 | Add-Content 'c:\scrap\combined.csv'
}
That should produce the exact same file. Only kind of tricky part was the LNNOTE/TMACCT/DMACCT field since obviously you can't just do the same as like *SHRT.