Reputation: 57
I have following code which is working correctly. Although I now need to modify the output in one specific column, so I can sort by this column correctly.
Here is my code:
$inputFile = "C:\Data\expPasswords\expPasswords.csv"
$outputFile = "C:\Data\expPasswords\expPasswordsUp.csv"
$result = Import-Csv $inputFile |
Select-Object @{ Name = 'Account'; Expression = { $_.Account } },
@{ Name = 'Days until Expiry'; Expression = { $_.'time until password expires' } },
@{ Name = 'Email address'; Expression = { $_.'email address' } }
# output on screen
$result | Sort-Object -Property 'Days until Expiry' | Format-Table -AutoSize
# output to csv
$result | Sort-Object -Property 'Days until Expiry' | Export-Csv -Path $outputFile -NoTypeInformation
I need to sort by the 'Days until Expiry' column. Although makes it hard when the output is as below:
0 minutes
0 minutes
1 day and 19 hours
1 day and 2 hours
1 day and 20 hours
1 day and 23 hours
13 hours
2 days
20 hours
Basically, what I would like to do is:
- If less than 1 day, make the value: Today
- Remove the hours and minutes blocks.
- So if it is 13 hours, make the value: Today
- If the value is 1 day and 1 hours and 35 minutes, make the value: 1 day
Any assistance will be greatly appreciated. ;-)
Upvotes: 1
Views: 506
Reputation: 447
I think the following might be of help (you will need to edit some of it, off course):
$Timings = @("0 minutes","0 minutes","1 day and 19 hours","1 day and 2 hours","1 day and 20 hours","1 day and 23 hours","13 hours","2 days","20 hours")
foreach ($Timing in $Timings) {
$Output = $null
if ($Timing -like "* minutes") {$Output = 0}
elseif ($Timing -like "* Day and * hours") {$Output = [int](($Timing).Split(' day')[0])}
elseif ($Timing -like "* hours") {$Output = 0}
else {$Output = [int](($Timing).Split(' day')[0]) }
switch ($Output) {
0 {$Result = "Today"}
1 {$Result = "Tomorrow"}
default {$Result = "Over $Output Days"}
}
Write-Output "$timing ==> $Result"
}
Upvotes: 1
Reputation: 61028
Its a shame you should spend time to make some sense out of this rather foolish output, but of course it can be done. Basically, all you want to do is find out if the string starts with a number followed by the word 'day' or 'days' and cut off all the rest. If this is not the case, the returned value should be 'Today'.
The easiest way to do that I think is by using switch -Regex
.
Try
$inputFile = "C:\Data\expPasswords\expPasswords.csv"
$outputFile = "C:\Data\expPasswords\expPasswordsUp.csv"
$result = Import-Csv $inputFile | ForEach-Object {
$daysLeft = switch -Regex ($_.'time until password expires') {
'^(\d+ days?)' { $matches[1] }
default { 'Today' }
}
[PsCustomObject]@{
'Account' = $_.Account
'Days until Expiry' = $daysLeft
'Email address' = $_.'email address'
}
} | Sort-Object -Property 'Days until Expiry'
# output on screen
$result | Format-Table -AutoSize
# output to csv
$result | Export-Csv -Path $outputFile -NoTypeInformation
Regex details:
^ Assert position at the beginning of the string \d Match a single character that is a “digit” (any decimal number in any Unicode script) + Between one and unlimited times, as many times as possible, giving back as needed (greedy) \ day Match the character string “ day” literally (case sensitive) s Match the character “s” literally (case sensitive) ? Between zero and one times, as many times as possible, giving back as needed (greedy)
Seeing your comment, I would suggest adding a real DateTime object to sort on.
Something like this:
$today = (Get-Date).Date
$result = Import-Csv 'D:\test.csv' | ForEach-Object {
$expiryString = $_.'time until password expires'
$expiryDate = $today
if ($expiryString -match '(\d+)\s*day') { $expiryDate = $expiryDate.AddDays([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*hour') { $expiryDate = $expiryDate.AddHours([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*minute') { $expiryDate = $expiryDate.AddMinutes([int]$matches[1]) }
if ($expiryString -match '(\d+)\s*second') { $expiryDate = $expiryDate.AddSeconds([int]$matches[1]) }
$daysLeft = if ($expiryDate.Date -eq $today) { 'Today' } else { ($expiryDate - $today).Days}
[PsCustomObject]@{
'Account' = $_.Account
'Email address' = $_.'email address'
'Days until Expiry' = $daysLeft
'Expiration Date' = $expiryDate
}
} | Sort-Object -Property 'Expiration Date'
# output on screen
$result
Output:
Account Email address Days until Expiry Expiration Date ------- ------------- ----------------- --------------- User1 [email protected] Today 6-4-2020 0:00:00 User6 [email protected] Today 6-4-2020 0:03:00 User8 [email protected] Today 6-4-2020 13:00:00 User4 [email protected] Today 6-4-2020 20:00:00 User9 [email protected] 1 7-4-2020 2:00:00 User2 [email protected] 1 7-4-2020 19:00:00 User5 [email protected] 1 7-4-2020 20:00:00 User7 [email protected] 1 7-4-2020 23:00:00 User3 [email protected] 2 8-4-2020 0:00:00
If you don't want that new property 'Expiration Date' in your output, simply filter it away with:
$result | Select-Object * -ExcludeProperty 'Expiration Date'
Upvotes: 2
Reputation: 23623
The constrains you defined will likely make it more confusing. I would just convert it to a [TimeSpan]
structure which makes it easy to sort:
$Result = ConvertFrom-Csv @'
"Account","Days until Expiry", "Email address"
"Account1","0 minutes", "[email protected]"
"Account2","1 day and 19 hours","[email protected]"
"Account3","2 days", "[email protected]"
"Account4","20 hours", "[email protected]"
"Account5","1 day and 20 hours","[email protected]"
"Account6","3 minutes", "[email protected]"
"Account7","1 day and 23 hours","[email protected]"
"Account8","13 hours", "[email protected]"
"Account9","1 day and 2 hours", "[email protected]"
'@
Function ConvertTo-TimeSpan([String]$String) {
$Days = If ($String -Match '\d+(?=\s*day)') {$Matches[0]} Else {0}
$Hours = If ($String -Match '\d+(?=\s*hour)') {$Matches[0]} Else {0}
$Minutes = If ($String -Match '\d+(?=\s*minute)') {$Matches[0]} Else {0}
$Seconds = If ($String -Match '\d+(?=\s*second)') {$Matches[0]} Else {0}
New-TimeSpan -Days $Days -Hours $Hours -Minutes $Minutes -Seconds $Seconds
}
$Result | Sort @{e = {ConvertTo-TimeSpan $_.'Days until Expiry'}}
Result:
Account Days until Expiry Email address
------- ----------------- -------------
Account1 0 minutes [email protected]
Account6 3 minutes [email protected]
Account8 13 hours [email protected]
Account4 20 hours [email protected]
Account9 1 day and 2 hours [email protected]
Account2 1 day and 19 hours [email protected]
Account5 1 day and 20 hours [email protected]
Account7 1 day and 23 hours [email protected]
Account3 2 days [email protected]
Upvotes: 0