Powershell compare months and exclude which are not required - powershell

I have a text file as shown below. I need to preserve the files which are within 6 months old (from the previous month) and write every other content to other file which are more than 6 months old.
What I've done so far is like:
$array3 =
do {
foreach($monthl in $monthlookup)
{
$mnthl = $monthl -split '-'
$m_day = $mnthl[1]
$m_month = $mnthl[2]
$m_year = $mnthl[3]
$m_temp = 0
$prevmonth = (Get-Date).AddMonths(-1).ToString("dd-MM-yy")
while(($m_temp -eq "0"))
{
if(($m_month -ge $startmonth) -and ($m_month -le $prevmonth))
{
$monthl | Add-Content "C:\each_month_latest.txt"
break;
}else
{
$monthl | Add-Content "C:\exclusions.txt"
}
}
}
} until ($m_month -ge $m)
What the issue I identified here is like: If the current month is 1, then it wont go back to the last 6 months and check, as I use just strings here.
Any suggestions or code improvements, anyone of you can think of..? Would be really appreciated.
Edit
monthlookup will look like:
testdatabase-30-11-14-23-00
testdatabase-31-12-14-23-00
testdatabase-30-01-15-23-00
testdatabase-31-01-15-23-00
testdatabase-27-05-15-23-00
testdatabase-28-02-15-23-00
testdatabase-31-03-15-23-00
testdatabase-30-04-15-23-00
testdatabase-31-05-15-23-00
$m is $m = Get-Date -Format "MM"

Well, I don't completly understand what you want to do but here some tipps:
$prevmonth = (Get-Date).AddMonths(-1).ToString("dd-MM-yy") shouldn't be done each time within the foreach loop
You could extract the date using a simple regex: [regex]::Match('testdatabase-30-11-14-23-00', '^.*?-(\d{2}-\d{2}-\d{2}-\d{2}-\d{2}$)').Groups[1].Value
Parse the date using [datetime]::ParseExact and a format string
So it could look like this:
# this doesn't have to be done each time in the foreach loop
$sixMonthBefore = (Get-Date).AddMonths(-6)
foreach($monthl in $monthlookup)
{
# extract the date
$dateString = [regex]::Match($monthl, '^.*?-(\d{2}-\d{2}-\d{2}-\d{2}-\d{2}$)').Groups[1].Value
# create a datetime using format string
$date = [datetime]::ParseExact($dateString, 'dd-MM-yy-HH-mm', $null)
if ($date.Month -eq (Get-Date).Month -and $date.Year -eq (Get-Date).Year)
{
Write-Host "$monthl is from this month"
}
elseif ($date -gt $sixMonthBefore)
{
$monthl | Add-Content "C:\each_month_latest.txt"
}
else
{
$monthl | Add-Content "C:\exclusions.txt"
}
}

Related

PowerShell script efficiency advice

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()

copying files from daily folders using Powershell

I am looking to move daily created files each day to another folder. These files are saved to the relevant YYYY\MM\folder each day. Now I have created a way to move these files over using the year/month date function, however because there a number attached to the month, i.e. December looks like "12. December" it becomes a little tricky.
I tried to amend this with an If statement which would assign "a" to the relevant number corresponding with the month however it doesnt work.
$year = (Get-Date).Year
$month = Get-Date -Format "MMMMMMMM"
$day = (Get-Date).Day
$a = ""
If ($month = "January") { $a = "1."}
Elseif ($month = "February") { $a = "2."}
Elseif ($month = "March") { $a = "3."}
Elseif ($month = "April") { $a = "4."}
Elseif ($month = "May") { $a = "5."}
Elseif ($month = "June") { $a = "6."}
Elseif ($month = "July") { $a = "7."}
Elseif ($month = "August") { $a = "8."}
Elseif ($month = "September") { $a = "9."}
Elseif ($month = "October") { $a = "10."}
Elseif ($month = "November") { $a = "11."}
Elseif ($month = "December") { $a = "12."}
$month = Get-Date -Format $a" MMMMMMMM"
Copy-Item -Path F:\BB\$year\$month\Scan.pdf -Destination F:\BB
Any idea how to fix this/where am i going wrong. This is my first time writing in Powershell.
Edit: I am getting an error in the file location it is copying to does not register the difference in the coressponding months. For example the if statement states that if the month is = December a should = 12. but its currently coming up as 1. which should be the case for if it were January
The different forms of the month may as well be repeated in the format string, where
M = month number without leading zeroes
MM = month number with leading zeroes
MMM = abbreviated month name
MMMM = full month name
So:
$Month = Get-Date -f "M. MMMM"
> $Month
12. December
As the format string can contain any letter you can build the source path in one step:
(escaped with a backslash if interfering with a format letter)
$Source = "F:\BB\{0:yyyy\\M. MMMM}\Scan.pdf" -f (Get-Date)
> $Source
F:\BB\2018\12. Dezember\Scan.pdf
But I'm missing the days here?
If you use
$month = Get-Date -Format "MM"
this will get you the month as a number. If I understand what you are trying to achive this should match you source path.
$Date = get-date
$Path = "F:\BB\" + "$($Date.year)" + "\" + "$($Date.month)" + "\"
Copy-Item -Path $Path -Destination F:\BB

Powershell ForEach-Object lines in a text file Split compare and delete

Im working on a Powershell script that can put lines from a text file into a list box, but only lines that start with a date ../../....
The add list box part is working. But id like to delete older lines from the text file first.
I tried this but it not correct. Can anyone help me code this correctly.
I will rephrase the question to hopefully make it clearer.
The text file look like this:
text.txt
//Regular Roster | Only update if making a permanent change.
!The order for the entry must be: Day time,number,name,email(optional)
!With a comma ‘,’ separating the values.
Monday 9AM-2PM,0400499449,BILL
Monday 2PM-6PM,074477464,Terry
Monday 6PM-9PM,040484747,Leanne
Tuesday 9AM-2PM,07123456,BILL
Tuesday 2PM-6PM,054647383,Barbara
Tuesday 6PM-9PM,03937464,Mandy
//Changes to Regular Roster. This section will override the above.
!The date order of the entries below is not important but each line must follow this pattern:
!date(Dayxx/Monthxx/Yearxxxx),time(9AM,2PM or 6PM),number,name,email(optional)
!Date slash must be forward facing '/'.
!The only times that can be entered are 9AM,2PM or 6PM.
!With a comma ‘,’ separating the values.
01/01/2019,6AM,0400012345,Kurt,kurt#outlook.com
02/01/2019,6AM,0412345676,Bill,bill#outlook.com
03/01/2019,6AM,0400012345,Sam,Sam#outlook.com
04/01/2019,6AM,0412345676,Barry,barry#outlook.com
05/01/2019,6AM,0400012345,Kurt,kurt#outlook.com
If I try this code in Powershell assuming the current date is 04/01/2019 (4th January 2019) it works fine
$CurrentDate = "04/01/2019"
$Text = "05/01/2019,6AM,0400012345,Kurt,kurt#outlook.com"
$Text | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
write-output "NewDate is bigger than or equal to"
}
else {
write-output "CurrentDate is bigger"
}
};
When I try to build a Listbox with this line it works fine
#Add Listbox
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $RosterPath | select-string -Pattern '../../...' | ForEach-Object {[void] $ShiftsListBox.Items.Add($_)};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
When I tried to combine the two it just comes up blank
#Add Listbox
$TextFile = text.txt
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $TextFile | select-string -Pattern '../../...' | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
[void] $ShiftsListBox.Items.Add($_)
}
else {
}
};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
The idea is that if the date is greater than or equal to the current date display the line in the list box, else don't. But then I will try to delete the line by adding in a replace line.
#Add Listbox
$TextFile = text.txt
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $TextFile | select-string -Pattern '../../...' | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
[void] $ShiftsListBox.Items.Add($_)
}
else {
((Get-Content -path $TextFile) -replace $_,"") | Set-Content -Path $TextFile;
}
};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
Based on new sample file, this script distinguishes
between lines with a leading date
a date from today or later(keep)
a date before today (skip)
other lines (keep)
## Q:\Test\2019\01\01\SO_53992011.ps1
$Rosterpath = ".\rosterpath"
$CurrentDate = (Get-Date).Date # to have a datetime starting today at 00:00:00
$RegEx = [RegEx]"^(?<day>\d{2})\/(?<mon>\d{2})\/(?<year>\d{4})"
$UpdatedContent = Get-Content $RosterPath | ForEach-Object {
If ($_ -match $RegEx){
If((Get-Date -Year $Matches.year -Month $Matches.mon -Day $Matches.day) -ge $CurrentDate){
# yes keep this line
$_
} else {
#drop this line
}
} else {
# line without a date, keep
$_
}
}
$UpdatedContent | Set-Content $RosterPath

Compare current date to date string in a file using powershell

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.

Filtering dates in PowerShell from CSV input

I've got a script that's causing me grief, where the where clause in the final pip isn't filtering dates out by the criteria I want.
The script is taking a CSV as an input, and the script parses the dates it finds so that they are in the correct date format.
The dates are then processed in a Where-Object clause to select all dates that are from within the past month onwards (all records with dates prior are not included). The problem I'm having is that this filter isn't working, and I'm getting all records included regardless.
My question is, what is wrong with my script and how can I fix this where clause.
Here's the code that correlates directly to the above:
if ($o.'Joined Company') {
$StartDate = [datetime]::ParseExact($o.'Joined Company','d/MM /yyyy',$null).ToString("dd/M/yyyy")
$o.StartDateObj = $StartDate
} elseif ($o.'Joined Company' -eq '') {
$o.StartDateObj = $null
}
if ($o.'last day of duty') {
$EndDate = [datetime]::ParseExact($o.'Last Day of Duty','d/MM/yyyy',$null).ToString("dd/M/yyyy")
$o.EndDateObj = $EndDate
} elseif ($o.'Last Day of Duty' -eq '') {
$o.EndDateObj = $null
}
Write-Debug ("" + $o.EndDateObj)
Write-Debug ("" + $o.StartDateObj)
}
$Today = Get-Date
$LastMonth = $Today.AddMonths(-1)
$obj | Where-Object {$_.Company -like "New Zealand"} |
Where-Object {($_.EndDateobj -gt $LastMonth) -or ($_.EndDateobj -eq $null)} |
It's just a snippit of the full script, which can be found here.