I have a textfile where I store my testname. The testname sometimes has a date afterwards consisting of a 14 digit timestamp. The testname can be:
data-c(festo1-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(festo1)-win1
or
data-c(festo1-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(festo1)-win1_20130620123306
I want to reuse this file/testname. I need to strip off the timestamp, if there is already one, and add the current timestamp. This is what I got so far:
#rescue testname for another run
$testname = Get-Content "output\testname"
Write-Host Old: $testname
#strip date from testname
$testname = $testname.Substring(0,$string.Length - 14)
$olddate = $testname.Substring($string.Length - 14)
Write-Host OldDate: $olddate
#add new Date
$startTime = Get-Date -format yyyyMMddHHmmss
$testname += "_"
$testname += $startTime
Write-Host New: $testname
How do I check if there is a timestamp/if the last 14 characters are digits? How do I cut the testname from the string?
this?
$filename = "data-c(festo1-small1);divider-bin-1.4.4;divider-conf-1.3.3-w(1,16);storage-bin-1.5.4;storage-conf-1.5.0-w(1);worker-bin-4.5.1;worker-conf-4.4.1-c(festo1)-win1_20130620123306"
$filename -replace '\d{14}$'
Related
I have created a text file that contains id and time[24hr format] that is going to trigger one operation at the mentioned time.
I want to read both the values and minus 2 hrs from mentioned hr.
I tried, but I am not sure how to read input as time format(24hr notation HH:MM) and minus 2 hrs from that and also the scenario like 01:30 - 2hr = 23.30 (previous date)
$lines = Get-Content C:\wamp64\www\schedule_daily_task.txt | Where {$_ -notmatch '^\s+$'}
foreach ($line in $lines) {
$fields = $line -split '\s+'
$id = $fields[0]
$time = $fields[1]
#code here to change the format of $time to time format and perform minus oper
#01:30 - 2hr = 23.30 (previous date)
}
For date and time arithmetics, use .Net's DateTime class. It has method AddHours() which, when passed negative values, subtracts hours. Calculating new datetime to yesterday is quite straight-forward. Like so,
# First, let's get today's date with specific time and minute
$hh = ($fields[1] -split ':')[0]
$mm = ($fields[1] -split ':')[1]
$dd = get-date -date $(get-date).date -hour $hh -minute $mm
# Add -2 hours
$dd.AddHours(-2)
I would use switch -Regex -File for this to read the file line-by-line and get the values from the lines you are after using regex.
Advantage is that this is crazy fast aswell.
$result = switch -regex -File 'C:\wamp64\www\schedule_daily_task.txt' {
'^(\d+)\s+(\d{2}:\d{2})' {
$id = $Matches[1]
$h, $m = $Matches[2] -split ':'
$time = '{0:HH:mm}' -f (Get-Date -Hour $h -Minute $m -Second 0).AddHours(-2)
# output the id and updated time
"$id $time"
}
default {
# output any other line you may have in the file. You can of course
# also ignore these if they are unwanted in the output. In that case
# simply comment out this whole 'default' block
$_
}
}
# output to (new) file and because of '-PassThru' also on screen
$result | Set-Content -Path 'C:\wamp64\www\schedule_daily_task_minus_2.txt' -PassThru
Example input file:
2603961 01:30
1234567 11:59
9876543 23:30
Example output file:
2603961 23:30
1234567 09:59
9876543 21:30
I have a telephony .csv with compiled data from January 2020 and some days of February, each row has the date and time spent on each status, since someone uses different status over the day the file has one row for each status, my script is supposed to go through the file, find the minimum date and then start saving on new files all the data for the same day, so I'll end with one file for 01-01-2020, 02-01-2020 and so on, but it has 15 hours running and it's still at 1/22.
The column I'm using for the dates is called "DateFull" and this is the script
write-host "opening file"
$AT= import-csv “C:\Users\xxxxxx\Desktop\SignOnOff_20200101_20200204.csv”
write-host "parsing and sorting file"
$go= $AT| ForEach-Object {
$_.DateFull= (Get-Date $_.DateFull).ToString("M/d/yyyy")
$_
}
Write-Host "prep day"
$min = $AT | Measure-Object -Property Datefull -Minimum
Write-Host $min
$dateString = [datetime] $min.Minimum
Write-host $datestring
write-host "Setup dates"
$start = $DateString - $today
$start = $start.Days
For ($i=$start; $i -lt 0; $i++) {
$date = get-date
$loaddate = $date.AddDays($i)
$DateStr = $loadDate.ToString("M/d/yyyy")
$now = Get-Date -Format HH:mm:ss
write-host $datestr " " $now
#Install-Module ImportExcel #optional import if you dont have the module already
$Check = $at | where {$_.'DateFull' -eq $datestr}
write-host $check.count
if ($check.count -eq 0 ){}
else {$AT | where {$_.'DateFull' -eq $datestr} | Export-Csv "C:\Users\xxxxx\Desktop\signonoff\SignOnOff_$(get-date (get-date).addDays($i) -f yyyyMMdd).csv" -NoTypeInformation}
}
$at = ''
The first loop doesn't make much sense. It loops through CSV contents and converts each row's date into different a format. Afterwards, $go is never used.
$go= $AT| ForEach-Object {
$_.DateFull= (Get-Date $_.DateFull).ToString("M/d/yyyy")
$_
}
Later, there is an attempt to calculate a value from uninitialized a variable. $today is never defined.
$start = $DateString - $today
It looks, however, like you'd like to calculate, in days, how old eldest record is.
Then there's a loop that counts from negative days to zero. During each iteration, the whole CSV is searched:
$Check = $at | where {$_.'DateFull' -eq $datestr}
If there are 30 days and 15 000 rows, there are 30*15000 = 450 000 iterations. This has complexity of O(n^2), which means runtime will go sky high for even relative small number of days and rows.
The next part is that the same array is processed again:
else {$AT | where {$_.'DateFull' -eq $datestr
Well, the search condition is exactly the same, but now results are sent to a file. This has a side effect of doubling your work. Still, O(2n^2) => O(n^2), so at least the runtime isn't growing in cubic or worse.
As for how to fix this, there are a few things. If you sort the CSV based on date, it can be processed afterwards in just a single run.
$at = $at | sort -Property datefull
Then, iterate each row. Since the rows are in ascending order, the first is the oldest. For each row, check if date has changed. If not, add it to buffer. If it has, save the old buffer and create a new one.
The sample doesn't convert file names in yyyyMMdd format, and it assumes there are only two columns foo and datefull like so,
$sb = new-object text.stringbuilder
# What's the first date?
$current = $at[0]
# Loop through sorted data
for($i = 0; $i -lt $at.Count; ++$i) {
# Are we on next date?
if ($at[$i].DateFull -gt $current.datefull) {
# Save the buffer
$file = $("c:\temp\OnOff_{0}.csv" -f ($current.datefull -replace '/', '.') )
set-content $file $sb.tostring()
# Pick the current date
$current = $at[$i]
# Create new buffer and save data there
$sb = new-object text.stringbuilder
[void]$sb.AppendLine(("{0},{1}" -f $at[$i].foo, $at[$i].datefull))
} else {
[void]$sb.AppendLine(("{0},{1}" -f $at[$i].foo, $at[$i].datefull))
}
}
# Save the final buffer
$file = $("c:\temp\OnOff_{0}.csv" -f ($current.datefull -replace '/', '.') )
set-content $file $sb.tostring()
I am have string which contain date and I want to compare that all last 15 days date with the date in string.
I have stored all 15 days date in array and comparing with the date in string.
#last 15 days date.
$Q = #()
$Q = for ($i=15; $i -gt 1; $i--) {
$date=Get-Date -DisplayHint Date #-Format "dd MMM yyyy"
$final=$date.AddDays(-$i)
$final.tostring("dd MMM yyyy")
}
# $array.'Description' have string like this "Enabled AD ID as per Call id: 509112 29 Oct 2019"
#comparing
if($array.'Description' -notcontains $Q){
Write-host ("true")
}else
{
write-host ("false")
}
I want comparing result.
You can split $array.'Description', then join the last three elements and then compare it with $Q.
($($array.'Description').Split(" ") | Select-Object -Last 3) -join " "
The comparison would look something like:
if ((($($array.'Description').Split(" ") | Select-Object -Last 3) -join " ") -notcontains $Q){
Write-host ("true")
}else
{
write-host ("false")
}
I think you should always compare dates to dates, not their string representation unless it is in Sortable format.
Below code takes the date string from the Description attribute and converts it to a local DateTime object to compare against:
$refDate = (Get-Date).AddDays(-15).Date # midnight 15 days ago
$array | ForEach-Object {
# parse a date object from the Description string
$dateString = ([regex]'(\d{1,2} \w{3} \d{4})$').Match($_.Description).Groups[1].Value
$date = [datetime]::ParseExact($dateString, 'd MMM yyyy', [cultureinfo]"en-US")
# make it a Local date
$date = [datetime]::SpecifyKind($date, 'Local')
if ($date -gt $refDate) {
# the description date is inside the last 15 days
Write-Host "True"
}
else { Write-Host "False" }
}
Edit
As per Esperanto57's comment, in case the Description string does not contain a date to parse, below uses a try{..} catch{..} block to tackle that:
$array | ForEach-Object {
try {
# parse a date object from the Description string
$dateString = ([regex]'(\d{1,2} \w{3} \d{4})$').Match($_.Description).Groups[1].Value
$date = [datetime]::ParseExact($dateString, 'd MMM yyyy', [cultureinfo]"en-US")
# make it a Local date
$date = [datetime]::SpecifyKind($date, 'Local')
if ($date -gt $refDate) {
# the description date is inside the last 15 days
Write-Host "True"
}
else { Write-Host "False" }
}
catch {
Write-Host "False. (The Description attribute does not contain a date.)"
}
}
I am writing some PS scripts to log times into a text file, login.txt, using the following code:
$logdir = "C:\FOLDER"
$logfile = "$logdir\LastLogin.txt"
$user = $env:USERNAME
$date = Get-Date -Format "dd-MM-yyyy"
if (!(Test-Path $logdir)){New-Item -ItemType Directory $logdir}else{}
if (!(Test-Path $logfile)){New-Item $logfile}else{}
if (Get-Content $logfile | Select-String $user -Quiet){write-host "exists"}else{"$user - $date" | Add-Content -path $logfile}
(Get-Content $logfile) | Foreach-Object {$_ -replace "$user.+$", "$user - $date"; } | Set-Content $logfile
This creates an entry in the text file like:
UserName - 01-01-1999
Using Powershell, I want to read the text file, compare the date, 01-01-1999, in the text file to the current date and if more than 30 days difference, extract the UserName to a variable to be used later in the script.
I would really appreciate any hints as to how I could do the following:
Compare the date in the text file to the current date.
If difference is more than 30 days, pick up UserName as a variable.
I would really appreciate any advice.
Checking all dates in the file with the help of a RegEx with named capture groups.
$logdir = "C:\FOLDER"
$logfile = Join-Path $logdir "LastLogin.txt"
$Days = -30
$Expires = (Get-Date).AddDays($Days)
Get-Content $logfile | ForEach-Object {
if ($_ -match "(?<User>[^ ]+) - (?<LastLogin>[0-9\-]+)") {
$LastLogin = [datetime]::ParseExact($Matches.LastLogin,"dd-MM-yyyy",$Null)
if ( $Expires -gt $LastLogin ) {
"{0} last login {1} is {2:0} days ago" -F $Matches.User, $Matches.LastLogin,
(New-TimeSpan -Start $LastLogin -End (Get-Date) ).TotalDays
}
}
}
Sample output
username last login 31-12-1999 is 6690 days ago
There is a way of doing that using regex (Regular Expressions). I will assume that the username which you get in your text file is .(dot) separated. For example, username looks like john.doe or jason.smith etc. And the entry in your text file looks like john.doe - 01-01-1999 or jason.smith - 02-02-1999. Keeping these things in mind our approach would be -
Using a regex we would get the username and date entry into a single variable.
Next up, we will split the pattern we have got in step 1 into two parts i.e. the username part and the date part.
Next we take the date part and if the difference is more than 30 days, we would take the other part (username) and store it in a variable.
So the code would look something like this -
$arr = #() #defining an array to store the username with date
$pattern = "[a-z]*[.][a-z]*\s[-]\s[\d]{2}[-][\d]{2}[-][\d]{4}" #Regex pattern to match entires like "john.doe - 01-01-1999"
Get-Content $logfile | Foreach {if ([Regex]::IsMatch($_, $pattern)) {
$arr += [Regex]::Match($_, $pattern)
}
}
$arr | Foreach {$_.Value} #Storing the matched pattern in $arr
$UserNamewithDate = $arr.value -split ('\s[-]\s') #step 2 - Storing the username and date into a variable.
$array = #() #Defining the array that would store the final usernames based on the time difference.
for($i = 1; $i -lt $UserNamewithDate.Length;)
{
$datepart = [Datetime]$UserNamewithDate[$i] #Casting the date part to [datetime] format
$CurrentDate = Get-Date
$diff = $CurrentDate - $datepart
if ($diff.Days -gt 30)
{
$array += $UserNamewithDate[$i -1] #If the difference between current date and the date received from the log is greater than 30 days, then store the corresponding username in $array
}
$i = $i + 2
}
Now you can access the usernames like $array[0], $array[1] and so on. Hope that helps!
NOTE - The regex pattern will change as per the format your usernames are defined. Here is a regex library which might turn out to be helpful.
I have a series of files named as such in a folder:
- myFile201801010703.file
I'm trying to parse out the yyyymmdd portion of each filename in the folder and sort them based on the date into an array.
So if I had the following files:
myFile201801200000.file (01/20/2018)
myFile201800100000.file (01/01/2018)
myFile201801100000.file (01/10/2018)
It would sort them into an array as such:
myFile201800100000.file (01/01/2018)
myFile201801100000.file (01/10/2018)
myFile201801200000.file (01/20/2018)
I have a process that works for file with timestamps included in the name, though have been unable to tweak it for work with only a date:
# RegEx pattern to parse the timestamps
$Pattern = '(\d{4})(\d{2})(\d{2})*\' + ".fileExtension"
$FilesList = New-Object System.Collections.ArrayList
$Temp = New-Object System.Collections.ArrayList
Get-ChildItem $SourceFolder | ForEach {
if ($_.Name -match $Pattern) {
Write-Verbose "Add $($_.Name)" -Verbose
$Date = $Matches[2],$Matches[3],$Matches[1] -join '/'
$Time = $Matches[4..6] -join ':'
[void]$Temp.Add(
(New-Object PSObject -Property #{
Date = [datetime]"$($Date) $($Time)" #If I comment out $($Time)it doesn't work.
File = $_
}
))
}
}
} catch {
Write-Host "`n*** $Error ***`n"
}
# Sort the files by the parsed timestamp and add to $FilesList
$FilesList.AddRange(#($Temp | Sort Date | Select -Expand File))
# Clear out the temp collection
$Temp.Clear()
The two lines in particular that I think might be culprit are:
$Time = $Matches[4..6] -join ':' Since I'm not parsing any time
Date = [datetime]"$($Date) $($Time)" Again, no time is parsed. Can't change the type to date either it seems?
With this format:
myFileYYYYMMddHHmm.file
the individual parts of the date and time is already arranged from largest (the year) to smallest (the minute) - this makes the string sortable!
Only thing we need to do is grab the last 12 digits of the file name before the extension:
$SortedArray = Get-ChildItem *.file |Sort-Object {$_.BaseName -replace '^.*(\d{12})$','$1'}
The regex pattern used:
^.*(\d{12})$
Can be broken down as follows:
^ # start of string
.* # any character, 0 or more times
( # capture group
\d{12} # any digit, 12 times
) # end of capture group
$ # end of string
The regex engine will expand $1 in the substitution string to "capture group #1", ie. the 12 digits we picked up at the end.