I am trying to trim down strings located inside of a .csv so that they only contain the data I need. An example string might be "\path\folder\folder\folder\folder Owner = John Doe".
I just want the John doe part.
Currently I'm importing a list of about 6000 of these strings some have the line some don't.
What I'm trying is:
$owners = Import-Csv .\Owners.csv
ForEach($owner in $owners){
if ($owner.Description -like "= *") {$owner.Description = "*"}
}
This doesn't seem to work. Is it possible to save the wildcard as a variable?
One method would be to use a Regular Expression rather than Wildcard globbing.
$owners = Import-Csv .\Owners.csv
$output = foreach ($owner in $owners) {
# The Description Property has an equal sign followed by a space
if ($owner.Description -like '*= *') {
# Use a regex then capture the the text after the equals and space, replace the property with that capture group.
$owner.Description = $owner.Description -replace '.*=\s(.*)$', '$1'
$owner
}
}
$output | Export-Csv .\Owners.update.csv -NoTypeInformation
You can do it with regex and match:
CSV:
Description
\path\folder\folder\folder\folder Owner = John Doe
\path\folder\folder\folder\folder Owner = Captain Black
Code:
$owners = Import-Csv .\Owners.csv
ForEach($owner in $owners){
$match = $owner.Description -match "= .*"
if ($match){
$owner.Description = $matches[0] -replace "= ", ""
}
echo $owner
}
Output:
Description
-----------
John Doe
Captain Black
Related
I have a CSV file where I have to find any non-blank value in 2 specific columns and replace them with 'Yes'
My data looks like this where it can have either both blank, value in either column, or in both.
Letter Grade
Numeric Grade
A
10
C
5
I want it to look like this when I'm done
Letter Grade
Numeric Grade
Yes
Yes
Yes
Yes
I have 2 problems, addressing columns that have a space in the name (tried wrapping with " and ' and {) and regex to match any non-empty value. It works with the code below to simply replace a and if the column is Letter instead of Letter Grade.
I tried .+ to match anything in the cell, but I get no matches.
Thanks in advance!
Import-Csv -Path ".\test.csv"| ForEach-Object {
if ($_.Letter -eq 'a') {
$_.Letter = 'Yes'
}
$_
} | Export-Csv .\poop2.csv -Encoding UTF8
You could handle this programmatically by, first, collecting all property names from the first object (done via accessing of intrinsic member PSObject in this example) and then enumerating each property of each object coming from the pipeline and checking if it matches \S (any non-whitespace character).
Import-Csv path\to\csv.csv | ForEach-Object { $isFirstObject = $true } {
if($isFirstObject) {
$properties = $_.PSObject.Properties.Name
$isFirstObject = $false
}
foreach($property in $properties) {
if($_.$property -match '\S') {
$_.$property = 'Yes'
}
}
$_
} | Export-Csv path\to\newcsv.csv -NoTypeInformation
If, instead of programmatically gathering the object's property names, you wanted to use specific / hardcoded properties, the code would be simpler:
$properties = 'Letter Grade', 'Numeric Grade'
Import-Csv path\to\csv.csv | ForEach-Object {
foreach($property in $properties) {
if($_.$property -match '\S') {
$_.$property = 'Yes'
}
}
$_
} | Export-Csv path\to\newcsv.csv -NoTypeInformation
I am having trouble with this.
What i am trying to do is I have 2 different array, and I am trying to exclude $items in $ignore from $users
I can't use where $users | where {$ignore -notcontains $_} because the string in the $ignore array may contain wildcard (e.g., admin*)
But here is the main problem, when I matched the $item that i want to remove, it removes my entire array instead of the selected value.
[System.Collections.ArrayList]$users = Get-ADUser -Filter * | Select Name
$users = $users.name
$ignore = $audArr[5] -Split(",")
foreach ($item in $ignore) {
if ($item.Contains("*")) {
foreach ($user in $users) {
$matchResult = $user -like $item
if ($matchResult -eq $true){
$users = $users.Remove($item)
}
}
}
else {
$users = $users.Remove($item)
}
}
However, I if i try to .Add it works despite showing error
Cannot convert the "3" value of type "System.Int32" to type "System.Collections.ArrayList".
At C:\users\administrator\desktop\scannerTest.ps1:326 char:17
+ $users = $users.Add($item)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : ConvertToFinalInvalidCastException
Also, since i am already asking about powershell items.. Does anyone know what how to get LogonTime, KickoffTime via powershell? Adn what is ACB?
Santiago Squarzon's helpful answer uses a good approach with regex matching. We can further enhance the regex matching to include a combination of wildcard and exact matches.
$users = 'John Doe','Wolverine','Mr. Pants','Administrator','Jane Doe','Master Administrator'
# two exact matches and one wildcard match
$ignore = 'john doe','jane doe','admin*'
$regex = '^({0})$' -f (($ignore -replace '\*','.*') -join '|')
# List users who don't match $ignore
$users -notmatch $regex # Jane Doe, John Doe, Administrator not listed
If you must use an ArrayList, you can apply the same technique as above by
removing successful matches:
[collections.ArrayList]$users = 'John Doe','Wolverine','Mr. Pants','Administrator','Jane Doe','Master Administrator'
# two exact matches and one wildcard match
$ignore = 'john doe','jane doe','admin*'
$regex = '^({0})$' -f (($ignore -replace '\*','.*') -join '|')
$users -match $regex | Foreach-Object {
[void]$users.Remove($_)
}
Explanation:
In regex, alternations (basic OR logic) are separated by |. .* greedily matches any number of characters in a string. ^ denotes the beginning of a string. $ denotes the end of a string. Putting that all together, your exact matches would look like ^exact$ and your partial matches would look like ^admin.*$ (for admin*) or ^.*admin.*$ (for *admin*). Using alternation, you can combine those matches for an OR effect with ^(admin.*|Benjamin Dover|Johnny Lawrence)$.
What you're trying to do could be simplified to something like this:
Create a test case, the array will have User00 to User10 plus Administrators and Domain Admins.
$ignore = #(
'Administrators'
'Domain Admins'
'User05'
'User10'
)
[System.Collections.ArrayList]$users = 0..10 |
ForEach-Object { [pscustomobject]#{Name="User{0:00}" -f $_} }
$users.Add([pscustomobject]#{Name="Administrators"})
$users.Add([pscustomobject]#{Name="Domain Admins"})
Now you can simply use the .Where(..) method filtering those elements of the $users array that do not match the $ignore array on one side and those that do match on the other side.
$toIgnore = $ignore -join '|'
$notIgnoredUsers, $ignoredUsers = $users.Where({$_.Name -notmatch $toIgnore},'Split')
$notIgnoredUsers
Name
----
User00
User01
User02
User03
User04
User06
User07
User08
User09
$ignoredUsers
Name
----
User05
User10
Administrators
Domain Admins
As for how you're using the .Remove(..) method, since it doesn't produce any output, by doing $users = $users.Remove($item) you're actually setting the variable to null:
PS /> [System.Collections.ArrayList]$arr = 'one','two','three'
PS /> $arr = $arr.Remove('one')
PS /> $null -eq $arr
True
If you want to remove one from the list you would simply do $arr.Remove('one').
I have a csv file that contains data in the below format:
Name Codes
------- ---------
John AJFKC,EFUY
Ben EFOID, EIUF
Alex OIPORE, OUOIJE
I would like to get the above in the below format and output it into a new csv file as below:
Name Codes
------- ---------
John AJFKC
John EFUY
Ben EFOID
Ben EIUF
Alex OIPORE
Alex OUOIJE
You can do the following if there will always be 2 codes per name:
Import-Csv file.csv | Foreach-Object {
$codes = ($_.Codes -split ',').Trim()
[pscustomobject]#{Name = $_.Name; Codes = $codes[0]}
[pscustomobject]#{Name = $_.Name; Codes = $codes[1]}
}
If the number of codes per name is unknown, you can do the following:
Import-Csv file.csv | Foreach-Object {
$codes = ($_.Codes -split ',').Trim()
foreach ($code in $codes) {
[pscustomobject]#{Name = $_.Name; Codes = $code}
}
}
If this is how this file really looks, it's not a Csv file. It's just a text file based copying and using what you posted. It's left-justified, vs columns, here the staggered look of it. The file has 5 spaces for that name column between it and that left justified Codes column with a code array.
So, though it looks like a table/tabular format, it's not really that. As in an import that name column would include the first of the code strings, thus cause some issues with your desired output. So, that text file would need to be manipulated a bit more, or who or whatever provides you the file, should make is a legitimate CSV.
So, here's my take on the assumptions in the aforementioned.
(Get-Content -Path 'D:\Temp\UserRecord.csv') -replace ', ',',' |
Select -Skip 2 |
ForEach {
$Name = (($PSItem) -split '\s+')[0]
($PSItem -split '\s+')[1] -split ',' |
ForEach {
[PSCustomObject]#{
Name = $Name
Codes = $PSItem
}
}
}
# Results
<#
Name Codes
---- -----
John AJFKC
John EFUY
Ben EFOID
Ben EIUF
Alex OIPORE
Alex OUOIJE
#>
try this:
Import-Csv "c:\temp\test.csv" -Delimiter "`t" | %{
$Name=$_.Name
$_.Codes -split "," | %{ [pscustomobject]#{Name=$Name;Code=$_.Trim()}}
}
I'm making a PowerShell script which queries our Office 365 tenants and exports certain information into a .csv file. The two fields I'm struggling with are the users default email address and their assigned subscriptions. I can get the data, but not sure how to manipulate it and make it look more presentable.
Get-MSOLUser -All | select firstname,lastname,displayname,islicensed,{$_.Licenses.AccountSkuId},{$_.proxyaddresses -cmatch '^SMTP\:.*'},userprincipalname | sort FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation
1) I've managed to get their primary email address as lower cased smtp addresses will always be aliases, but how do I strip out the "SMTP:" part?
2) Instead of "reseller-account:SKU Part Number" I was hoping to shorten this to the names we usually refer them as! Such as:
"E3" instead of "reseller-account:ENTERPRISEPACK"
"E5" instead of "reseller-account:ENTERPRISEPREMIUM"
"ProjectPro" instead of "reseller-account:PROJECTPROFESSIONAL"
"Visio" instead of "reseller-account:VISIOCLIENT"
Two questions really but very similar! Hope you can help.
To Achieve that you can use Calculated Properties along with a small function to convert the SkuId's to a Friendly name and using -replace to remove the SMTP part , I've Created for you a simple function for the conversion, you can add other products just like i did:
The Microsoft Product Name/SKU's list can be found in this link
function Convert-SkuIdToFriendlyName
{
Param(
[string]$SkuId
)
switch ($SkuId)
{
{$SkuId -match "ENTERPRISEPACK"} {return "OFFICE 365 ENTERPRISE E3"}
{$SkuId -match "ENTERPRISEPREMIUM"} {return "OFFICE 365 ENTERPRISE E5"}
default { 'Unknown' }
}
}
Then use a Calculated properties to replace the 'SMTP' part and convert the SkuId:
Get-MSOLUser -All |
Select firstname,lastname,displayname,islicensed,
#{N="License";E={Convert-SkuIdToFriendlyName $_.Licenses.AccountSkuId}},
#{N="Email";E={$_.proxyaddresses -cmatch '^SMTP\:.*' -replace 'SMTP\:'}},userprincipalname |
Sort FirstName
You can use a hashtable as a lookup table for the wanted translations like so:
# create a hash with the desired translations.
# below are just the examples from your question. You need to fill in the rest..
$Licenses = #{
"ENTERPRISEPACK" = "E3"
"ENTERPRISEPREMIUM" = "E5"
"PROJECTPROFESSIONAL" = "ProjectPro"
"VISIOCLIENT" = "Visio"
}
Get-MSOLUser -All |
Select-Object firstname,lastname,displayname,islicensed,userprincipalname,
#{ Name = 'License'; Expression = { $Licenses[$(($_.Licenses.AccountSkuId) -replace '^.+:', '')] }},
#{ Name = 'PrimaryEmailAddress'; Expression = { ($_.proxyaddresses -cmatch '^SMTP\:.*') -replace "SMTP:", "" }} |
Sort-Object FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation
In order to get all licenses a user can have listed, the code could be extended to:
# create a hash with the desired translations for the license plans.
# below are just the examples from your question. You need to fill in the rest..
$Licenses = #{
"ENTERPRISEPACK" = "E3"
"ENTERPRISEPREMIUM" = "E5"
"PROJECTPROFESSIONAL" = "ProjectPro"
"VISIOCLIENT" = "Visio"
}
# this calculated property returns all (translated) licenses per user as comma delimited string
$userLicenses = #{
Name = 'Licenses'
Expression = {
$result = #()
foreach($lic in $_.Licenses) {
$result += $Licenses[$(($lic.AccountSkuId) -replace '^.+:', '')]
}
$result -join ', '
}
}
Get-MSOLUser -All |
Select-Object firstname,lastname,displayname,islicensed,userprincipalname,$userLicenses,
#{ Name = 'PrimaryEmailAddress'; Expression = { ($_.proxyaddresses -cmatch '^SMTP\:.*') -replace "SMTP:", "" }} |
Sort-Object FirstName | Export-Csv $directory\$tenantname\Export.csv -NoTypeInformation
The subject is my ultimate goal. Using Powershell I have managed to return the contents of "ipconfig /displaydns" formatted into a table but I used some publicly posted code and I am new to powershell so I don't fully understand how it all works. The problem with what I am using is that it only returns the results that begin with the text "Record Name". This means that any entry that has no A record or where a record doesn't exist will not be returned. I'm using this code to try and get these records:
Function Get-FailedDNSClientCache{
Invoke-Expression "IPConfig /DisplayDNS" | Select-String -Pattern "-----" -Context 2,1 |
ForEach-Object{
"" + $_.Context.PreContext[1]
"" + $_.Line
"" + $_.Context.Postcontext[0]
"" + $_.Context.PreContext[2]
}
}
The code above gives me some output that looks like this:
pagead.l.doubleclick.net
----------------------------------------
No records of type AAAA
s0-2mdn-net.l.google.com
----------------------------------------
Record Name . . . . . : s0-2mdn-net.l.google.com
www.redditstatic.com
----------------------------------------
No records exist
s0-2mdn-net.l.google.com
----------------------------------------
Record Name . . . . . : s0-2mdn-net.l.google.com
I've been trying all the different filtering methods I have found but I end up returning no data, null fields or I filter out just the text line that starts with "Record Name" but not the entire record. I think the problem is that I need to create each returned record as an object rather than each line but my attempts at that have failed as well. I'd basically like for my output to look like this:
pagead.l.doubleclick.net
----------------------------------------
No records of type AAAA
www.redditstatic.com
----------------------------------------
No records exist
I've seen bits and pieces of my question answered throughout the forum and I have tried most of the solutions provided but I think I am not understanding something fundamental allowing me to put it all together correctly. Please keep in mind I am truly trying to understand what I am doing so the more information the better.
TIA
You can use the Get-DnsClientCache cmdlet and pass in the status flag you wish to search for. For example:
Get-DnsClientCache -Status NotExist
Or
Get-DnsClientCache -Status NoRecords
If you don't have this function, you can create your own based on https://gallery.technet.microsoft.com/scriptcenter/ad12dc1c-b0c7-44d6-97c7-1a537b0b4fef
The code for the above cited function is:
Function Get-DNSClientCache{
$DNSCache = #()
Invoke-Expression "IPConfig /DisplayDNS" |
Select-String -Pattern "Record Name" -Context 0,5 |
%{
$Record = New-Object PSObject -Property #{
Name=($_.Line -Split ":")[1]
Type=($_.Context.PostContext[0] -Split ":")[1]
TTL=($_.Context.PostContext[1] -Split ":")[1]
Length=($_.Context.PostContext[2] -Split ":")[1]
Section=($_.Context.PostContext[3] -Split ":")[1]
HostRecord=($_.Context.PostContext[4] -Split ":")[1]
}
$DNSCache +=$Record
}
return $DNSCache
}
Either of these methods will return an object that you can manipulate further as needed.
Use Where-Object to filter on matches where the PostContext[0] value contains the string "No records":
Function Get-FailedDNSClientCache{
Invoke-Expression "IPConfig /DisplayDNS" | Select-String -Pattern "-----" -Context 2,1 | Where-Object {$_.Context.Postcontext[0] -like "*No records*"} |ForEach-Object {
"" + $_.Context.PreContext[1]
"" + $_.Line
"" + $_.Context.Postcontext[0]
"" + $_.Context.PreContext[2]
}
}
Using a simple regex:
oneliner
([regex]'(?s)(\S+)\n\s+-+\s+(.+?)\n').matches((ipconfig /displayDNS) -join "`n") | ?{ $_.groups[2].value -notlike 'Record Name*' } | %{ $orphans = #{} } { $orphans[$_.groups[1].value] = $_.Groups[2].value } { $orphans }
more readable, using PS2 SortedDictionary
$output = (ipconfig /displayDNS) -join "`n"
$rx = [regex]'(?s)(\S+)\n\s+-+\s+(.+?)\n'
$orphansSorted = [Collections.Generic.SortedDictionary[string,string]]::new()
forEach ($match in $rx.matches($output)) {
$url = $match.groups[1].value
$msg = $match.groups[2].value
if ($msg -notlike 'Record Name*') {
$orphansSorted[$url] = $msg
}
}
$orphansSorted
Output:
Key Value
--- -----
mpa.one.microsoft.com No records of type AAAA
my.router No records exist
onlineconfigservice.ubi.com No records of type AAAA
static3.cdn.ubi.com No records of type AAAA
ubisoft-orbit.s3.amazonaws.com No records of type AAAA
ubisoft-orbit-savegames.s3.amazonaws.com No records of type AAAA