Comparing strings inside of foreach - string

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

Related

Comparing lines, -notcontains gets ignored

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

PowerShell script to monitor IIS logs for 500 errors every 10 minutes

I'm trying to set up a script to monitor IIS 7.5 logs fro 500 errors. Now I can get it to do that OK but I would like it to check every 30 minutes. Quite naturally I don't want it to warn me about the previous 500 errors it has already reported.
As you can see from the script below I have added a $time variable to take this into account, however I can't seem to find a way to use this variable. Any help would be appreciated.
#Set Time Variable -30
$time = (Get-Date -Format hh:mm:ss (Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select time,csuristem,scstatus
OK With KevinD's help and PowerGUI with a fair bit of trial and error, I got it working as I expected. Here's the finished product.
#Set Time Variable -30
$time = (Get-Date -Format "HH:mm:ss"(Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "C:\Users\here\Documents\IIS-log\"+"u_ex"+(get-date).ToString("yyMMdd")+".log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select #{n="Time"; e={Get-Date -Format "HH:mm:ss"("$($_.time)")}},csuristem,scstatus | ? { $_.time -ge $time }
Thanks again Kev you're a good man. Hope this code helps someone else out there.
Here's
Try changing your last line to:
$IISLog | select #{n="DateTime"; e={Get-Date ("$($_.date) $($_.time)")}},csuristem,scstatus | ? { $_.DateTime -ge $time }
In the select, we're concatenating the date and time fields, and converting them to a date object, then selecting rows where this field is greater than your $time variable.
You'll also need to change your $time variable:
$time = (Get-Date).AddMinutes(-30)
You want a DateTime object here, not a string.

Compare objects in an if statement Powershell

I'm trying to compare two files and if their content matches I want it to preform the tasks in the if statement in Powershell 4.0
Here is the gist of what I have:
$old = Get-Content .\Old.txt
$new = Get-Content .\New.txt
if ($old.Equals($new)) {
Write-Host "They are the same"
}
The files are the same, but it always evaluates to false. What am I doing wrong? Is there a better way to go about this?
Get-Content returns an array of strings. In PowerShell (and .NET) .Equals() on an array is doing a reference comparison i.e. is this the same exact array instance. An easy way to do what you want if the files aren't too large is to read the file contents as a string e.g.:
$old = Get-Content .\Old.txt -raw
$new = Get-Content .\Newt.txt -raw
if ($old -ceq $new) {
Write-Host "They are the same"
}
Note the use of -ceq here to do a case-sensitive comparison between strings. -eq does a case-insensitive compare. If the files are large then use the new Get-FileHash command e.g.:
$old = Get-FileHash .\Old.txt
$new = Get-FileHash .\New.txt
if ($old.hash -eq $new.hash) {
Write-Host "They are the same"
}

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