I have a log file that I have to scan every 5 minutes for specific keywords - specifically "Order Failed" then capture the lines around that. I have that part programmed with no issues. The rest of the log entries don't matter. I'm able to use the command:
[string] $readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
which outputs:
Message: Order submission failed.
Timestamp: 4/1/2016 4:05:09 PM
Severity: Error
Message: Modifier not authorized for parent item
Message: Order submission failed.
Timestamp: 4/1/2016 4:18:15 PM
Severity: Error
Message: Modifier not authorized for parent item
Which is exactly what I want. My problem is trying to read through the above output and store the "Timestamp" into a variable that I can manipulate.
The first challenge is the "Timestamp" time is written in UTC time and we are located in the Pacific Time Zone.
The second challenge is I need to compare the "Timestamp" time to the current time and store that as an integer. We only want to report errors that are within 5 minutes of the current time.
My current code only grabs the first "Timestamp" entry since I hard-coded it:
[string] $readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
$fileArray = $readFile.Split(“`n”)
[dateTime] $TrimTime = $fileArray[3].trim("Timestamp: ")
[dateTime] $currentTime = get-date
[int32] $getMinutes = (new-timespan -start $TrimTime -end $currentTime).minutes
I don't know how to loop through the output of the Get-content - to check for all of the Timestamps - we only want to report errors that are within 5 minutes of the current time.
Don't cast your matches to a string right away.
$readFile = get-content c:\test\friday.log | select-String -pattern '(order.*failed)' -context 1,7
If you leave the MatchInfo objects intact for the time being you can extract the timestamps from the aftercontexts like this:
$readFile | ForEach-Object {
$_.Context.AfterContext | Where-Object {
$_ -match 'timestamp: (\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}:\d{2} [ap]m)'
} | ForEach-Object {
$timestring = $matches[1]
}
}
Use the ParseExact() method to convert the matched substring to a DateTime value:
$fmt = 'M\/d\/yyyy h:mm:ss ttK'
$culture = [Globalization.CultureInfo]::InvariantCulture
$timestamp = [DateTime]::ParseExact("${timestring}Z", $fmt, $culture)
The Z that is appended to $timestring indicates the UTC timezone and the trailing K in the format string makes the method recognize it as such. The result is automatically converted to your local time. If you need the time in UTC: append .ToUniversalTime() to the method call.
Related
I’m using PowerShell with a script to convert a .CSV raw data file into more manageable data format with separate columns, a cleaner view etc. And because the source file with the raw data is in US date and time format (e.g. 11/23/21, 1:00 PM), then if the PC is in that same US format the conversion process runs perfectly as should with 0 errors. BUT, if the PC is in a different country date and time format, then PowerShell shows errors in red in the process.
When the PC is in other DateTime format I see the main error is:
"Parse" with "1" argument(s): "String was not recognized as a valid DateTime."
And the problem is the PC where this will be used is not in US format (only changed to US format for testing), so could someone here please help me to add to the conversion process the syntax or sentence/s to simply specify directly in the code a fixed format that keeps a static output format independently about the PC clock date and time format, and if one of the inputs into the file is “11/23/21, 1:00 PM” then to specify in the code you want the output in the format “dd-MMM-yyyy hh:mm” to have a result like “23-Nov-2021 01:00 PM”
The code section in the script used for the conversion is:
…
$data = $csvData | ? {$_ -match "\(DTRE"}
dtreFileData = New-Object System.Collections.Generic.List[PSCustomObject]
foreach ($item in $data)
{
$null = $item.Strategy -match "\(DTRE\|(.*)\)"
$v = $Matches[1] -split '\|'
$resultvalue = $v[0] | Convert-CurrencyStringToDecimal
$expectedvalue = $v[1] | Convert-CurrencyStringToDecimal
$dtreData = [PSCustomObject]#{
'DateTime' = ([datetime]::Parse($item.'Date/Time'))
'ResultValue' = [decimal]$resultvalue
'ExpectedValue' = [decimal]$expectedvalue
}
$null = $dtreFileData.Add($dtreData)
$null = $dtreAllData.Add($dtreData)
}
$dtreFileData | Export-Csv -Path (Join-Path (Split-Path -Path $f -Parent) ($outFile + '.csv')) -Force -NoTypeInformation -Encoding ASCII
…
Example of raw source data (in the CVS file are dozens of lines like the next one):
...(DTRE|49.0|48.2);...;11/23/21, 12:58 PM...;
...(DTRE|52.1|52.0);...;11/23/21, 1:00 PM...;
...
...
And the Output looks like:
I tried with DateTime examples in other posts from here (stackoverflow.com) to adjust the code to work in a PC without US date and time format and to get the DateTime format result described above. Examples like:
'DateTime' = ([datetime]::Parse($item.'yyyy-MM-dd:HH:mm:ss'))
'DateTime' = ([datetime]::ParseExact($item.'yyyy-MM-dd:HH:mm:ss'))
…
$culture = [Globalization.CultureInfo]::InvariantCulture
…
'DateTime' = ([datetime]::ParseExact($item.'yyyy-MM-dd:HH:mm:ss', $culture))
…
But with these examples PowerShell shows the error “Cannot bind argument to parameter 'InputObject' because it is null”
Update after the answer from #Seth:
When trying next modification of the code, with the PC system date format in “24-Nov-21” and leaving the rest as above:
…
$resultvalue = $v[0] | Convert-CurrencyStringToDecimal
$expectedvalue = $v[1] | Convert-CurrencyStringToDecimal
$cultureInfo= New-Object System.Globalization.CultureInfo("es-ES")
$dtreData = [PSCustomObject]#{
'DateTime' = ([System.DateTime]::Parse($item.'Date/Time', $cultureInfo))
'ResultValue' = [decimal]$resultvalue
'ExpectedValue' = [decimal]$expectedvalue
…
then, PowerShell shows the next errors:
As it has been explained it's a good idea to fix the CSV to have a better dateformat. An example would be ISO 8601 which can be used with Get-Date -Format "o".
That said Get-Date relies on C# stuff in the background. So you can use C# code to read that in a particular culture. As you know the origin culture this should work. Fixing the timestamp is still a better idea.
$cultureInfo= New-Object System.Globalization.CultureInfo("en-US")
$dateString = "11/23/21, 12:58 PM";
$dateTime = [System.DateTime]::Parse($dateString, $cultureInfo);
Get-Date -Format "o" $dateTime
With this example code you'd assign $dateString the value of $item.' Date/Time' and the result you likely want would be the result of Get-Date. So you'd assign $dtreData.'DateTime' the result of that Get-Date call. Alternatively it is possible to use the .NET DateTime Object to directly convert to a particular culture. For instance by calling $dateTime.ToString((New-Object System.Globalization.CultureInfo("en-ES"))). Though not all that useful you could also pass the format identifier to this method. This might be relevant if you want to avoid creating additional objects. A somewhat unnecessary call would be $dateTime.ToString("o", (New-Object System.Globalization.CultureInfo("en-ES"))) (as format o is the same in every culture).
I have a (plain text) log file with dates that appear in most (but not all) lines of text. The date does not always appear in the same position on each line. Though not all lines have a date, there is always a date in any given set of 10 lines. A sample of the log:
03/05/2019 Event A occurred
03/05/2019 Event B occurred
Event B Details: Details Details
03/07/2019 Event C occurred
Logging completed on 03/08/2019
03/08/2019 Event D occurred
I need to get the date of last item logged. I don't need the content of the line, just the date that's in the line.
#Get the log file. Tail it because it's huge. The regex pattern matches date format dd/mm/yyyy.
$log = (Get-Content -tail 10 C:\mylog.log | Select-String -Pattern "\d{2}/\d{2}\/d{4}"
#Get the last item in the $log variable. Convert it to a string.
$string = $log[-1].toString()
#Split the string. Match each element to a date format.
#When an element matches a date format, assign its value to a DateTime variable.
foreach ($s in $string.split() ){
if ($s -match "\d{2}/\d{2}\/d{4}"){
[DateTime]$date = $s
}
}
"The last entry in the log was made on $date"
Both parts of this code (finding the last line with a date, and pulling the date from the line) seem really kludgy. Is there a better way to do this?
You may do the following:
[datetime]((Get-Content mylog.log -tail 10) |
Select-String '\d{2}/\d{2}/\d{4}')[-1].Matches.Value
Select-String returns a collection MatchInfo objects. .Matches.Value of each object contains the matched string. Using the [-1] index, we can grab the last object.
Note: Select-String has a -Path parameter that can read a file. But given you don't want to read the entire file, Get-Content -Tail is used.
I'm trying to make a code that automates creating a scheduled task by pulling information from an .ini file using schtasks.exe in powershell. However the starttime parameter has a HH:mm format.
It doesn't accept the time in string format (invalid starttime value) and the : interferes with converting it to an integer.
I pulled the hours and minutes as separate variables (and converted them to integers) and tried to "connect" them with a : which gives me variable reference and unexpected token errors.
I've pulled the time from the .ini and removed the :, converted it to an integer, and then tried the .insert(2,':') which gives me
"invocation failed, int 32 does not contain a method named insert"
The code itself-
schtasks.exe /RU $username /RP $password /CREATE /SC DAILY /TN 'My Task' /TR 'powershell.exe C:\mycode.ps1' /ST $time
Process of pulling the time form the .ini-
$pull = Get-Content -Path 'C:\Info.ini' | Select -Skip 2 | ForEach-Object {$_ -replace '\D+(\d+)','$1'}
$inttime = [int]$pull
$time = $inttime.Insert(2,':')
Is there any way for me to get the : between the integers after pulling them from the file? Thank you.
You are currently attempting to use the Insert() method from String class on an Int32 object. You will need to cast the variable as a string or convert the value to a string to use the method.
$time = ([string]$inttime).Insert(2,':')
# OR
$time = $inttime.ToString().Insert(2,':')
If all of the digits you are trying to capture are in groups of 4, you can just perform this insert with your -replace operator.
$pull = Get-Content -Path 'C:\Info.ini' |
Select-Object -Skip 2 |
ForEach-Object { $_ -replace '\D+(\d{2})(\d{2})','$1:$2' }
I want yesterday's date as "MMdd", like "0418" today's date is "0419".
I tried this:
$d = (Get-Date).AddDays(-1) | select -Property Month,Day
$e = $d.Day
$d = $d.Month
$total = "$d" + "$e"
Write-Host $total
But the output was "418".
As you can see, this is a three-letter string, but I want a four-letter one. Because I want to search files they have that format and created a day before.
The date object has a method ToString, which allows you to specify the format:
$d = (Get-Date).AddDays(-1);
$d.ToString("MMdd");
I am calling this on April the 20th, and the output is
0419
See this link for a description of the available date format strings.
Novice at PS here, but I have a log file where each entry is written on a separate line with this date format: 2013-04-29 08:55:09,261
I am trying to use PowerShell to delete all lines older than 30 days. I have been trying to plug get-date -format "yyyy-MM-dd hh" output with some sort of greater than code, but at this point I’m just guessing. Also been trying forfiles with a batch file but would like to stick with PS if I can.
Any help would be much appreciated.
Assuming a file with the contents:
2013-04-29 08:55:09,261 line1
2013-01-29 08:55:09,261 line2
2013-03-31 08:55:09,261 line3
For me your dates are in the international standard date notation so you can just use :
$a = [datetime]"2013-04-29 08:55:09"
Then $a will be a date, so nothing to do with the culture.
You can just write the following to filter all lines from a date (here "2013-03-31")
get-content "C:\temp\date.txt" | where { [datetime]($_.split(','))[0] -ge "2013-03-31"}
I just split the line on the coma, take the first part and convert it to a date before compararing it.
For your 30 days (get-date).date give the date without hours and (get-date).date.adddays(-30) give the date 30 days before today.
get-content C:\temp\date.txt | where { [datetime]($_.split(','))[0] -ge (get-date).date.adddays(-30)}
You can pipe the result in a new file | set-content "C:\temp\newdate.txt"
Try this:
gc .\logfile.txt | %{if ([datetime][regex]::match($_, '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}').Value -gt (Get-Date).AddDays(-30)) {$_}} > purgedlogfile.txt
Breakdown and explanation of the components:
.\logfile.txt is the path to the log file
^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3} is a regular expression that matches your log file's timestamp format at the beginning of each line
[regex]::match($_, '^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}').Value returns the timestamp matched by the regular expression
Preceding it with the [datetime] typecast operator converts it to a DateTime object (in order to compare it to another DateTime)
(Get-Date) returns a DateTime object representing the current date and time, and .AddDays(-30) subtracts 30 days from it, to return a DateTime object representing 30 days ago
For each line in the logfile, the if block prints the line if the DateTime representing that line's timestamp is greater than the DateTime representing 30 days ago (i.e., the timestamp is more recent than 30 days), otherwise it ignores the line
Assuming a file with the contents:
2013-04-29 08:55:09,261 line1
2013-01-29 08:55:09,261 line2
2013-03-31 08:55:09,261 line3
You could get your desired output with:
$culture = [System.Globalization.CultureInfo]::InvariantCulture
$format = 'yyyy-MM-dd HH:mm:ss,fff'
Get-Content .\test.txt | ? { [datetime]::ParseExact(([string]$_).Substring(0,23), $format, $culture) -ge (Get-Date).AddDays(-30) }
Adi Inbar's answer is good, but if you'd prefer to avoid regular expressions, here's another way:
Get-Content e:\pathto\logfile.txt | Where-Object { ( (Get-Date) - (Get-Date $_.Substring(0,10)) ).Days -le 30} | Add-Content e:\pathto\NewLog.txt