PowerShell Replacing text in CSV - powershell

I am building a script to pull information from one system and place it into a CSV for another system to pull it. I have to do some data cleanup before that though. I am pulling userid's from the system and they come out as dom\userid. For my other system to use it i have to have just the userid with no dom. I used the below code to remove dom and this works:
$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
This leaves me with \userid. As soon as I add the \ to the replace string, the system freaks because it tries to interpret the \ and can't do a replace so I tried the following
$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM`\', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
I get the same error
ERROR: Regular expression pattern is not valid: DOM_WFLD\.
broker.ps1 (24): ERROR: At Line: 24 char: 54
ERROR: + $csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -repl ...
ERROR: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~
ERROR: + CategoryInfo : InvalidOperation: (DOM_WFLD:String) [], RuntimeException
ERROR: + FullyQualifiedErrorId : InvalidRegularExpression
I can't seem to get this to work. Does anyone have any ideas?

On the line that contains $csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM\', '' } }`
It could be simplified and made more versitale.
$csv | %{ $_.LastConnectionUser -replace '\w+\\'}
If statement for something like this is not really required since the action you are taking doesnt significantly increase processing time. Also your IF will match if a user name contains DOM which i imagine was not your intention.
Which would replace all characters infront of a \ including the \ itself. As David explains you need to escape the \ as it is a special chracter in regular expressions. -replace with no second parameter automatically just removes the match.

$csv = Import-Csv C:\Support\Broker.csv
$csv | %{ If ($_.LastConnectionUser -like "*DOM*") { $_.LastConnectionUser -replace 'DOM\\', '' } }
$csv | Export-Csv C:\Support\Broker.csv -NoTypeInformation -Force
The escape you have to use is the regular expression one (so backslash), not the PowerShell one (back tick)

Related

Powershell 5: ConvertTo-Csv a CSV with quotes in some but not all columns

I am building am updating a script which imports a large CSV file and then splits it into lots of separate CSV files based on the value in the first two columns
so POIMP_NL_20210306.csv which contains:
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
must be split into POIMP_P-100-1234_JANE.csv containing
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
and POIMP_P-100-6666_TED.csv
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
The problem I am trying to solve is preserving the quotes in just the SUPPLIER column
Since ConvertTo-Csv adds quotes to everything, I use a % { $_ -replace '"', ""} to remove these all before the out-file is created but of course it removes these from the SUPPLIER column 2
Here is my script which perfectly splits the big file into smaller files by DOC_NUMBER and COMMENTS but removes all quotes:
$basePath = "C:\"
$archivePath = "$basePath\archive\"
$todaysDate = $(get-date -Format yyyyMMdd)
$todaysFiles = #(
(Get-ChildItem -Path $basePath | Where-Object { $_.Name -match 'POIMP_' + $todaysDate })
)
cd $basePath
foreach ($file in $todaysFiles ) {
$fileName = $file.ToString()
Import-Csv $fileName -delimiter "|" | Group-Object -Property "DOC_NUMBER","COMMENTS" |
Foreach-Object {
$newName = $_.Name -replace ",","_" -replace " ",""; $path=$fileName.SubString(0,8) + $newName+".csv" ; $_.group |
ConvertTo-Csv -NoTypeInformation -delimiter "|" | % { $_ -replace '"', ""} | out-file $path -fo -en ascii
}
Rename-Item $fileName -NewName ([io.path]::GetFileNameWithoutExtension("$fileName") + "_Original.csv")
Move-Item (Get-ChildItem -Path $basePath | Where-Object { $_.Name -match '_Original' }) $archivePath -force
}
And here is another script which I found online and amended and which successfully leaves quotes in just the SUPPLIER column by first adding double back ticks and then replacing these with quotes after all others have been removed
$ImportedCSV = Import-CSV "C:\POIMP_NL_20210306.csv" -delimiter "|"
$NewCSV = Foreach ($Entry in $ImportedCsv) {
$Entry.SUPPLIER = '¬¬' + $Entry.SUPPLIER + '¬¬'
$Entry
}
$NewCSV |
ConvertTo-Csv -NoTypeInformation -delimiter "|" | % { $_ -replace '"', ""} | % { $_ -replace '¬¬', '"'} | out-file "C:\updatedPO.csv" -fo -en ascii
I just can't merge these scripts to achieve the desired result as I can't seem to reference the correct object. I'd really appreciate your help! Thanks
Any good CSV reader should be able to handle quotes around csv fields, even when not really needed.
Having said that, It is your explicit wish to only have quotes around the field in the SUPPLIER column. (Note, in your example there is a trailing space after that column name)
In this case, I think this would help.
Not only does it surround the SUPPLIER fields with quotes, but also saves the data as separate files using the values from column DOC_NUMBER and COMMENTS per group found in the csv
$path = 'D:\Test'
$fileIn = Join-Path -Path $path -ChildPath 'POIMP_NL_20210306.csv'
# import the csv file and group first two columns
Import-Csv -Path $fileIn -Delimiter '|' | Group-Object -Property "DOC_NUMBER","COMMENTS" | ForEach-Object {
$headerDone = $false
$data = foreach ($item in $_.Group) {
if (!$headerDone) {
$item.PsObject.Properties.Name -join '|'
$headerDone = $true
}
$item.SUPPLIER = '"{0}"' -f $item.SUPPLIER
$item.PsObject.Properties.Value -join '|'
}
# create a new filename like 'POIMP_P-100-1234_JANE.csv'
$fileOut = Join-Path -Path $path -ChildPath ('POIMP_{0}_{1}.csv' -f $_.Group[0].DOC_NUMBER, $_.Group[0].COMMENTS)
# save the data not using Export-Csv because that will add quotes around everything (in PowerShell 5)
$data | Set-Content -Path $fileOut -Force
}
Output
POIMP_P-100-1234_JANE.csv
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-1234|JANE|5059585896978|2|"JOES SUPPLIES"
P-100-1234|JANE|5059585896985|2|"JOES SUPPLIES"
POIMP_P-100-6666_TED.csv
DOC_NUMBER|COMMENTS|ITEM|QTY|SUPPLIER
P-100-6666|TED|5059585896992|1|"ACTION TOYS"
If you are Powershell 7 or later, you can use
$yourdata | ConvertTo-Csv -NoTypeInformation -QuoteFields "SUPPLIER" -Delimiter "|" |
Out-File ...
or you could use
$yourdata | Export-Csv -NoTypeInformation -QuoteFields "SUPPLIER" `
-Delimiter "|" -Path <path-to-output-file>.csv
You can also use -UseQuotes AsNeeded to let the converter add quoting where it thinks it makes sense, otherwise just specify the fields you want quoted.

Delete line if it includes a specific string

I have a text file where it will say the computer name and current date they logged in.
04/10/2017, "PC1"
04/10/2017, "PC4"
05/10/2017, "PC3"
09/10/2017, "PC2"
I'm having issues trying to run a script that will look for any line that includes "PC2" and delete that line :
get-content "c:\file.csv" | %{if($_ -match "PC2"){$_ -replace $_, ""}} | set-content c:\file.csv
(Get-Content 'C:\File.csv') -notmatch 'PC2' | Set-Content 'C:\File1.csv'
You can also use regex
File extension is csv
Import-Csv 'C:\File.csv' -Header Logged,Computer |
where {$_.Computer -ne 'PC2'} |
Export-Csv 'C:\File.csv' -NoClobber -NoTypeInformation
(Get-Content -Path 'C:\File.csv') |
Where-Object { $_ -notlike '*PC2*' } |
Set-Content -Path 'C:\File.csv'
Here you go. This utilizes an easier-to-understand wildcard comparison operator and just filters out the lines that have the matched string.

Select-String -Pattern $variable issue

I'm trying to create a report by cross referencing two text documents. I have C:\formeremployees.txt and C:\shareaudit.txt. As you can guess the formeremployees.txt has a list of former employee usernames only. No Headers; only usernames. The C:\shareaudit.txt contains a list of every folder on a share with the ACL info on the same line next to the folder path.
Here was my attempt at creating a report that only lists the lines that have user accounts from the formeremployees.txt:
$Users = Get-Content C:\formeremployees.txt
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
But unfortunately, I get the following error:
Select-String : Cannot bind argument to parameter 'Pattern' because it is an
empty string.
At line:7 char:71
+ $Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Select-String : Cannot bind argument to parameter 'Pattern' because it is an
empty string.
At line:7 char:71
+ $Output = Select-String -Path "C:\ShareAudit.txt" -Pattern "$User"
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [Select-String], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Microsoft.PowerShell.Commands.SelectStringCommand
Then I get a sad, empty completereport.txt file. I can't seem to get this to work or know if it's possible.
Edit________________________
Here's what else I've tried and the results:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
This gave me a blank C:\completereport.txt document.
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
$Pattern = ($Users|ForEach{[regex]::escape($_)}) -join '|'
Get-Content "C:\Shareaudit.txt" | Where{$_ -match $Pattern} | Set-Content C:\completereport.txt
This as far as I can tell didn't do anything. There was no completereport.txt document created when it finished.
$Users=(Get-Content C:\formeremployees.txt) -ne ''
foreach ($User in $Users) {
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
$Output.Line | Out-File C:\completereport.txt -Append
}
This gave me a blank text document.
$Output = Select-String -Path "C:\Shareaudit.txt" -Pattern "<single username from formeremployeee.txt>"
$Output.Line | Out-File C:\completereport.txt -Append
When I put in a username that I knew still had permissions to some folders in the share and was also in the formeremployee.txt, the script worked as intended and gave me a list of the folders I needed so there's nothing wrong with the bottom part of the script, so I'm guessing something is up with the formeremployee.txt or the way I used the $Users variable.
To test further, I tried this:
$Users=(Get-Content C:\formeremployees.txt) -ne ''
foreach ($User in $Users) {
Select-String -Path "C:\Shareaudit.txt" -Pattern "$User"
}
This didn't output any results. The text formeremployee.txt file lists the usernames as follows:
username1
username2
username3
username4
Is it in the wrong format for this?
The most obvious answer is that you have blank lines in your FormerEmployee.txt file. The simplest solution is to update your first line:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
What I would probably do to speed things up is make a regex pattern out of the users, and run the Select-String once, instead of once per user:
$Users = Get-Content C:\formeremployees.txt|?{!([string]::IsNullOrWhitespace($_))}
$Pattern = ($Users|ForEach{[regex]::escape($_)}) -join '|'
Get-Content "C:\Shareaudit.txt" | Where{$_ -match $Pattern} | Set-Content C:\completereport.txt

powershell - output rows in a .csv, based on conditions of each entry in a column

I'm new to powershell and have had very little coding experience.
Apologies for the initial reading, I was trying to put into context what I'm attempting to do, but to make it easier, I have reduced the wall of text. and added the source files.
My sincere apologies...
I've been tasked with creating a script to verify user accounts against an output .csv from another server.
The importing .csv will have additional columns in it, but I am having trouble filtering on usernames column.
First, I output the columns I need $'Machine Name' and $'User Name' to "$ADUserList", removing the "" around each entry, with:
$SourceFile1 | select $_'Machine Name', $_'User Name' | ConvertTo-Csv -Delimiter ',' -NoTypeInformation | foreach { $_ -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }|'
This is where I have issues:
I then import "$ADUserList" to attempt to output each row that matches the expected user format ( 8digits long, all numbers) to "GoodADUser.csv" and those that don't match to "BadADUser.csv".
Failure point:
$ADUserList = Import-Csv $PSScriptRoot\Output\ADUserList.csv
ForEach-Object {$item -in $ADUserList
IF ($SourceFile1.'User Name'.Length -eq 8 -and $SourceFile1.'User Name' -is [int])
{$item | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { $i -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }| Out-File $PSScriptRoot\Output\GoodADUserList.csv -Force
}
Else
{$item | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { $i -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }| Out-File $PSScriptRoot\Output\BadADUserList.csv -Force
}
}
At this point, I get no file output and get an error message:
False
ConvertTo-Csv : Cannot validate argument on parameter 'Delimiter'. The argument is null. Provide a valid value for the argument, and then try running the command again.
At C:\Users\Administrator\Desktop\Drive Encryption Script\Quick Test.ps1:17 char:39
+ {$item | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { ...
+ ~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [ConvertTo-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ConvertToCsvCommand
I've uploaded the script and sample files here: https://1drv.ms/f/s!Ajx8h3iSP9tuoAz7A37RBUvO7H5H
Thanks guys, really appreciate the help...
Here's a couple corrections to your script.
You probably will have better luck passing the object through a pipe to the for each method. When you do that the default object variable is $_
$ADUserList = Import-Csv $PSScriptRoot\Output\ADUserList.csv
$ADUserList | ForEach-Object {
$item = $_
IF ($SourceFile1.'User Name'.Length -eq 8 -and $SourceFile1.'User Name' -is [int])
{$item | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { $i -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }| Out-File $PSScriptRoot\Output\GoodADUserList.csv -Force
}
Else
{$item | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { $i -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }| Out-File $PSScriptRoot\Output\BadADUserList.csv -Force
}
From your downloaded files, this will split into "(username is 8 characters and numeric)" and "(everyone else)"
$users = Import-Csv machineusers.csv
$goodAccounts = #()
$badAccounts = #()
foreach ($user in $users)
{
if (($user.'User Name'.Length -eq 8) -and
([int]::TryParse($user.'User Name', [ref]$null)))
{
$goodAccounts += $user
}
else
{
$badAccounts += $user
}
}
$goodAccounts | Select 'User Name', 'Machine Name' | Export-CSV good.csv -NoTypeInformation
$badAccounts | Select 'User Name', 'Machine Name' | Export-CSV bad.csv -NoTypeInformation
I don't know the intent of all the lines which mangle the CSV, but please don't do that, there must be a better way to achieve it.

Retrieve text from a file in ps script

I have a text file containing some data as follows:
test|wdthe$muce
check|muce6um#%
How can I check for a particular string like test and retrieve the text after the | symbol to a variable in a PowerShell script?
And also,
If Suppose there is variable $from=test#abc.com and how to search the file by splitting the text before "#" ?
this may be one possible solution
$filecontents = #'
test|wdthe$muce
check|muce6um#%
'#.split("`n")
# instead of the above, you would use this with the path of the file
# $filecontents = get-content 'c:\temp\file.txt'
$hash = #{}
$filecontents | ? {$_ -notmatch '^(?:\s+)?$'} | % {
$split = $_.Split('|')
$hash.Add($split[0], $split[1])
}
$result = [pscustomobject]$hash
$result
# and to get just what is inside 'test'
$result.test
*note: this may only work if there is only one of each line in the file. if you get an error, try this other method
$search = 'test'
$filecontents | ? {$_ -match "^$search\|"} | % {
$_.split('|')[1]
}
First you need to read the text from the file.
$content = Get-Content "c:\temp\myfile.txt"
Then you want to grab the post-pipe portion of each matching line.
$postPipePortion = $content | Foreach-Object {$_.Substring($_.IndexOf("|") + 1)}
And because it's PowerShell you could also daisy-chain it together instead of using variables:
Get-Content "C:\temp\myfile.txt" | Foreach-Object {$_.Substring($_.IndexOf("|") + 1)}
The above assumes that you happen to know every line will include a | character. If this is not the case, you need to select out only the lines that do have the character, like this:
Get-Content "C:\temp\myfile.txt" | Select-String "|" | Foreach-Object {$_.Line.Substring($_.Line.IndexOf("|") + 1)}
(You need to use the $_.Line instead of just $_ now because Select-String returns MatchInfo objects rather than strings.)
Hope that helps. Good luck.
gc input.txt |? {$_ -match '^test'} |% { $_.split('|') | select -Index 1 }
or
sls '^test' -Path input.txt |% { $_.Line.Split('|') | select -Index 1 }
or
sls '^test' input.txt |% { $_ -split '\|' | select -Ind 1 }
or
(gc input.txt).Where{$_ -match '^test'} -replace '.*\|'
or
# Borrowing #Anthony Stringer's answer shape, but different
# code, and guessing names for what you're doing:
$users = #{}
Get-Content .\input.txt | ForEach {
if ($_ -match "(?<user>.*)\|(?<pwd>.*)") {
$users[$matches.user]=$matches.pwd
}
}
$users = [pscustomobject]$users