Comparing lines, -notcontains gets ignored - string

I want to parse a .dat file with plain text (strings and values) in it.
For example, I want the Value behind "NAME:". But there also is a line with "FAM.NAME:" in it.
But I only want the one with NAME.
This is what I tried:
$content= Get-Content $_.FullName
foreach($line in $content){
if($line -contains "NAME" -and $line -notcontains "FAM" ) { $test= $line }
}
As far as I understand, the condition is that the line contains "NAME" AND does NOT contain "FAM".
The Output is "FAM.NAME: ALFREDO". It seems to ignore the condition after the -and. Why is that?

you can also use the -like operator -
PS>"FAM.NAME: ALFREDO" -like "NAME*"
False
PS>"NAME: ALFREDO" -like "NAME*"
True
Note - make sure to add the * to match the rest of the string, otherwise it won't match

You need to use -match and -notmatch to compare strings, instead of -contains and -notcontains which operate on arrays.
$content= Get-Content $_.FullName
foreach($line in $content){
if($line -match "NAME" -and $line -notmatch"FAM" ) { $test= $line }
}

Related

How can I replace a string in multiple files with a value from a list, sequentially in Powershell?

Lets say I have a bunch of text files with people's names, that all have this as the content:
number
I want to replace "number" with a value from a CSV or text file, sequentially, and based on the file name. CSV has two columns, name and number:
Joe 5551011000
Gary 5551011001
Clark 5551011002
So I want to find the text file named Joe, and replace the "number" with "5551011000", and the text file named Gary, and replace "number" with "5551011001".
Thank you!
I didn't get too far:
Get-ChildItem "C:\test\*.txt" -Recurse | ForEach-Object -Process {
(Get-Content $_) -Replace 'changeme', 'MyValue' | Set-Content $_
}
This gets me party there, but I don't know how to find a specific file, then replace "number" in that file with the correct value that matches the name.
I also tried a different approach, with manual entry, and it works, but I need it to just be automated:
get-childitem c:\Marriott -recurse -include *.txt |
select -expand fullname |
foreach {
$new = Read-Host 'What is the new value you want for ' $_
(Get-Content $_) -replace 'number',$new |
Set-Content $_
}
I would convert your CSV to a hashtable, then this gets pretty simple.
$ReplaceHT = #{}
Import-Csv c:\path\to\file.csv -Delimiter ' ' -Header 'FileName','Number' | ForEach-Object {$ReplaceHT.add($_.FileName,$_.Number)}
Get-ChildItem c:\Marriott -recurse -include *.txt -PipelineVariable 'File'|Where{$_.name -in $ReplaceHT.Keys} |ForEach-Object{
(Get-Content $File.FullName) -replace 'changeme', $ReplaceHT[$File.Name] | Set-Content $File.FullName
}

Remove matching collection object from text file

I have a list of users that I am storing in a text file. I am trying to update the text file so it removes any user that match $NotExpiring users variable, which is a collection. I just can't figure out how I would update the text file properly if more than one user needs to be removed from text file.
Below is the full function. You can ignore most of it Just look under #Stuck Here to get to the point.
function Get-NotExpiring{
$NotExpiring=New-Object System.Collections.Generic.List[System.Object]
$MatchedUser=New-Object System.Collections.Generic.List[System.Object]
$textfiles = Get-ChildItem $email_dir
#Day of Span
$Days="20"
#Settings
$Date=Get-Date ((Get-Date).adddays($Days))
$Users=Get-ADUser -filter {(Enabled -eq $True) -and (PasswordNeverExpires -eq $False)} -Properties SamAccountName, DisplayName, msDS-UserPasswordExpiryTimeComputed, Mail | Where-Object { $_.DisplayName -ne $nul -and ($_."msDS-UserPasswordExpiryTimeComputed" -gt ($NotExpDate.ToFileTime()))} | Select SamAccountName, Mail, DisplayName,#{Name="ExpiryDate";Expression={([datetime]::fromfiletime($_."msDS-UserPasswordExpiryTimeComputed")).DateTime}}
#Magic
foreach ($Entry in $Users) {
$EntryDate = Get-date($Entry.ExpiryDate)
if ($EntryDate -gt $Date){
$Account = $Entry.SamAccountName
$ExpDate = $Entry.ExpiryDate
$NotExpiring.add($Account)
}
}
#STUCK HERE
foreach($file in $textfiles){
foreach ($user in $NotExpiring){
if((Get-Content "$email_dir\$file") -contains $user){
$temp_get = Get-Content $email_dir\$file | where {$_ -notmatch $user}
}}}
$temp_get}
I tried below but it doesn't seem to work if more than one user are $NotExpiring that are also in the existing textfile. Any help would be appreciated. I know this is a simple fix but I can't seem to figure it out.
Get-Content $email_dir\$file | where {$_ -notmatch $user} | Set-Content <path>.txt
I was able to achieve exactly what I needed using the following solution.
foreach($file in $textfiles){ foreach ($user in $NotExpiring){
if((Get-Content "$email_dir\$file") -contains $user){
$MatchedUser.add($user)
}}
Get-Content "$email_dir\$file" | Where {$MatchedUser -NotContains $_ } | Set Content "$temp_dir\$file"
Copy-Item -path "$temp_dir\$file" -Destination "$email_dir\$file" -ErrorAction SilentlyContinue }
Basicly you are trying to match two arrays.
With where you do it foreach object. Now you have to match the single object $_ with the array $user.
Use:
...| where {$_ -notin $user}
or
...| where {$user -notcontains $_}

Comparing strings inside of foreach

I want to compare 2 strings in PowerShell. One is the actual date and the other will be read from a file that contains a lot of rows. The row always will contain the same structure, then we can extract a substring for compare the date.
The file will be a plain text. The types of a date and the substring are the same.
MyProblem:
If I execute this code the program doesn't write anything, the Write-Host is not executing, even if the strings are the same. Can someone help me?
$list = Import-Csv C:\file.txt
#actual date
$date = Get-date -Format d
$day = $fecha.Substring(0,2)
$month = $fecha.Substring(3,2)
$year = $fecha.Substring(6,4)
$date = "$year$month$day"
#I do this because if I use $list will return me an pscustomobject object
$file = Get-Content -Path C:\file.txt
#Use a ForEach loop to process all lines in the source file
foreach ($row in $file) {
$sub = $entrada.Substring(7,7)
if ($date-like $sub) {Write-Host "They are equals"}
if ($sub -Match $date) {Write-Host "They are equals"}
if ($date.Equals($sub)) {Write-Host "They are equals"}
if ($date-eq $sub) {Write-Host "They are equals"}
if ($sub -contains $date) {Write-Host "They are equals"}
}
Your reference date string is 8 characters long, but the string you extract from the lines has only 7 characters, so it's unlikely you'll ever get a match. Particularly since you carefully chose your comparisons to avoid even accidental matches. ;) Also, as #arco444 pointed out in the comments to your question, your loop variable $row is never used anywhere inside the loop.
I would suggest to simplify the code to something like this:
$date = Get-Date -f 'yyyyMMdd'
Get-Content -Path 'C:\file.txt' | Where-Object { $_.Substring(7,8) -eq $date }
That would list only those lines from the input file that contain a matching date.
Another option would be to use the Contains() method on each line:
Get-Content -Path 'C:\file.txt' | Where-Object { $_.Contains($date) }
but that would find a matching date anywhere in a line, not just at the given position.
I'd avoid using wildcard (-like) or regular expression (-match) checks, since you want to compare a fixed value, not a pattern. And you can't use the -contains operator, because that one is for checking if an array contains a particular element.
It looks like "entrada" and "row" were intended to refer to the same data item.
Try it like this:
$list = Import-Csv C:\file.txt
#actual date
$date = Get-date -Format d
$day = $fecha.Substring(0,2)
$month = $fecha.Substring(3,2)
$year = $fecha.Substring(6,4)
$date = "$year$month$day"
#I do this because if I use $list will return me an pscustomobject object
$file = Get-Content -Path C:\file.txt
#Use a ForEach loop to process all lines in the source file
foreach ($entrada in $file) {
$sub = $entrada.Substring(7,7)
if ($date-like $sub) {Write-Host "They are equals"}
if ($sub -Match $date) {Write-Host "They are equals"}
if ($date.Equals($sub)) {Write-Host "They are equals"}
if ($date-eq $sub) {Write-Host "They are equals"}
if ($sub -contains $date) {Write-Host "They are equals"}
}

Replacing value on one line in a text file

I am currently working on editing one line of a text file. When I try to overwrite the text file, I only get one line back in the text file. I am trying to call the function with
modifyconfig "test" "100"
config.txt:
check=0
test=1
modifyConfig() function:
Function modifyConfig ([string]$key, [int]$value){
$path = "D:\RenameScript\config.txt"
((Get-Content $path) | ForEach-Object {
Write-Host $_
# If '=' is found, check key
if ($_.Contains("=")){
# If key matches, replace old value with new value and break out of loop
$pos = $_.IndexOf("=")
$checkKey = $_.Substring(0, $pos)
if ($checkKey -eq $key){
$oldValue = $_.Substring($pos+1)
Write-Host 'Key: ' $checkKey
Write-Host 'Old Value: ' $oldValue
$_.replace($oldValue,$value)
Write-Host "Result:" $_
}
} else {
# Do nothing
}
}) | Set-Content ($path)
}
The result I receive in my config.txt:
test=100
I am missing "check=0".
What have I missed?
$_.replace($oldValue,$value) in your innermost conditional replaces $oldValue with $value and then prints the modified string, but you don't have code printing non-matching strings. Because of that only the modified string are written back to $path.
Replace the line
# Do nothing
with
$_
and also add an else branch with a $_ to the inner conditional.
Or you could assign $_ to another variable and modify your code like this:
Foreach-Object {
$line = $_
if ($line -like "*=*") {
$arr = $line -split "=", 2
if ($arr[0].Trim() -eq $key) {
$arr[1] = $value
$line = $arr -join "="
}
}
$line
}
or a one liner.. (not exactly pin pointed answer, but to the question title)
(get-content $influxconf | foreach-object {$_ -replace "# auth-enabled = false" , "auth-enabled = true" }) | Set-Content $influxconf

Powershell filter a List by Name and Date

I need a bit of help... I'm new to powershell and i want to Filter a List (csv). I would love to remove all lines with certain names in it. and cut the list down to the last month. In the script you can see how far i got till now.
param(
[Parameter(ValueFromPipeline=$true,HelpMessage="Enter CSV path(s)")]
[String[]]$Path = $null
)
if($Path -eq $null) {
Add-Type -AssemblyName System.Windows.Forms
$Dialog = New-Object System.Windows.Forms.OpenFileDialog
$Dialog.InitialDirectory = "$InitialDirectory"
$Dialog.Title = "Select CSV File(s)"
$Dialog.Filter = "CSV File(s)|*.csv"
$Dialog.Multiselect=$true
$Result = $Dialog.ShowDialog()
if($Result -eq 'OK') {
Try {
$Path = $Dialog.FileNames
}
Catch {
$Path = $null
Break
}
}
else {
Write-Host -ForegroundColor Yellow "Notice: No file(s) selected."
Break
}
}
$info=Import-Csv "$path" -Delimiter ';'
$info | Get-Member
$info | Format-Table
as you can see i tryed to link the path to a filebrowser.
For the purposes of discussion, I will assume that the full pathname of the CSV is in the variable $InputPath, and that you want to write the result to a CSV file whose full pathname is in the variable $OutputPath. I will also assume that the CSV file contains a column named 'Name', and that the value from the Name column that you want to exclude is in the variable $ExcludedName. Given that, you can simply do
Import-CSV -Path $InputPath | Where-Object {$_.Name -ne $ExcludedName} | Export-CSV -Path $OutputPath -NoTypeInformation
You can do this by my code,but dont forget that first row must contains names of column and delimiter must be ';' and $nameslist is array of names that you need delete:
$info=Import-Csv "D:\testdir\file2.csv" -Delimiter ';'
$nameslist=#('James','John','andrew')
foreach($i in $info){
if($nameslist -contains $i.Name){
$i.Name=""
}
$i|Export-Csv -Path "D:\testdir\file1.csv" -Delimiter ';' -NoTypeInformation -Force -Encoding UTF8 -Append
}
Try this:
$data = Import-Csv "Path" | Select-Object * -ExcludeProperty Names
$data | export-csv "Path" -Notype
This will cut the column names.
Try it first without using a function:
Import-Csv <Filename> | Where-Object {$_.<FieldName> -notlike "*<Value>*"}
Also, you might consider something like this:
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true, HelpMessage = "Enter CSV path(s)")]
[String[]]$Path = $(
Add-Type -AssemblyName System.Windows.Forms
$DialogProperties = #{
Title = 'Select CSV File(s)'
Filter = 'CSV File(s)|*.csv'
Multiselect = $True
}
$Dialog = New-Object System.Windows.Forms.OpenFileDialog -Property $DialogProperties
$Dialog.ShowDialog()
If ($Result -eq 'OK') {
$Path = $Dialog.FileNames
} Else {
Write-Error 'Notice: No file(s) selected.'
}
)
)
Process {
ForEach ($PathItem in $Path) {
Import-Csv $PathItem | Where-Object { $_.Name -notlike "*NotThisOne*" }
}
}

Resources