0

I would like to take the following string and parse it in a way to output the length of each step. Time is listed as hr:min:sec.

Edited Desired Output:

[Step 2] took 2 minutes to complete.

[Test 3] took 3 minutes and 3 seconds to complete.

[Example 4] took 3 minutes to complete.

edited to ask that each line item in brackets of $content is output individually rather than simply listing "Step"

$Content =

[Step1]::14:37:11
[Step2]::14:39:11
[Test3]::14:42:14
[Example4]::14:45:14
2
  • 1
    What's content here? Raw text? An array? Commented May 20, 2020 at 21:55
  • is the time shown the start or the end of each step? your desired result indicates that the times are the END of each step. Commented May 21, 2020 at 12:43

2 Answers 2

1

In this case, I would create an array of TimeSpan objects from the strings in $content and use these for the output.

IF $content is an array, use it straight away.
IF $content is a single string, you need to split it first with $content = $content -split '\r?\n'

# assuming $content is a single string, so we need to split into array:
$Content = @"

[Step1]::14:37:11
[Step2]::14:39:11
[Step3]::14:42:14
[Step4]::14:45:09
[Step5]::14:57:12
"@ -split '\r?\n'

Next loop through this string array and remove empty lines if there are any:

$times = $content | Where-Object { $_ -match '\S' } | ForEach-Object {
    $h, $m, $s = ($_.Trim() -replace '.*(\d{2}:\d{2}:\d{2})$', '$1') -split ':'
    [TimeSpan]::new([int]$h, [int]$m, [int]$s)
}

for ($i = 1; $i -lt $times.Count; $i++) {
    $timeDiff = $times[$i] - $times[$i - 1]
    # output the times taken for each step
    "Step $($i + 1) took $($timeDiff.ToString()) to complete."
}

Output:

Step 2 took 00:02:00 to complete.
Step 3 took 00:03:03 to complete.
Step 4 took 00:02:55 to complete.
Step 5 took 00:12:03 to complete.

Regex details for -replace:

.            Match any single character that is not a line break character
   *         Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
(            Match the regular expression below and capture its match into backreference number 1
   \d        Match a single digit 0..9
      {2}    Exactly 2 times
   :         Match the character “:” literally
   \d        Match a single digit 0..9
      {2}    Exactly 2 times
   :         Match the character “:” literally
   \d        Match a single digit 0..9
      {2}    Exactly 2 times
)
$            Assert position at the end of the string (or before the line break at the end of the string, if any)


As per your new requirements in the comment, here's the somewhat altered code:

$Content = @"

[Step1]::14:37:11
[Step2]::14:39:11
[Test3]::14:42:14
[Example4]::14:45:14
"@ -split '\r?\n'

$times = $content | Where-Object { $_ -match '\S' } | ForEach-Object {
    # capture the action like '[Example4]' and the timestamp part
    $action, $processTime = $_.Trim() -split '::'
    # split the timestamp into Hours, Minutes, Seconds 
    $h, $m, $s = ($processTime -replace '.*(\d{2}:\d{2}:\d{2})$', '$1') -split ':'
    # output an object with two properties, the action name and a TimeSpan
    [PsCustomObject]@{
        'Action' = $action
        'Time'   = [TimeSpan]::new([int]$h, [int]$m, [int]$s)
    }
}

# loop through the returned array, get the time difference between steps
for ($i = 1; $i -lt $times.Count; $i++) {
    $timeDiff = $times[$i].Time - $times[$i - 1].Time
    # output the times taken for each step, in this new requirement rounded to show minutes only
    '{0} took {1} minutes to complete' -f $times[$i].Action, [math]::Round($timeDiff.TotalMinutes, 0)
}

Output:

[Step2] took 2 minutes to complete
[Test3] took 3 minutes to complete
[Example4] took 3 minutes to complete
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, Theo!
Added an additional request to my initial question marked it bold... could you please take a look at this Theo?
0

here's a slightly different way to get there ... [grin]

what it does ...

  • creates a set of time info lines to work with
    when ready to do this for real, remove the entire #region/#endregion block and replace it with a call to Get-Content.
  • filters out the lines that don't have a time info string
  • iterates thru the lines starting @ 1 and ending @ the upper bound of the collection
    this skips [step1] since your desired output indicates the timing info is the END of each step.
  • grabs the start time from the previous array item
  • grabs the step name and end time from the current array item
  • builds a string using the -f string format operator
  • calcs the elapsed time by converting the time info to timespan objects & subtracting the start from the end
  • sends that out to the screen

i was tempted to put the info into an array of PSCustomObjects, but you seem to want only the string output. [grin]

the code ...

#region >>> fake reading in a text file
#    in real life, use Get-Content
$InStuff = @'
$Content =

[Step1]::14:37:11
[Step2]::14:39:11
[Step3]::14:42:14
[Step4]::14:45:09
[Step5]::14:57:12
'@ -split [System.Environment]::NewLine
#endregion >>> fake reading in a text file

# get rid of the no-time-info lines
$TimeLines = $InStuff -match '::'

foreach ($Index in 1..$TimeLines.GetUpperBound(0))
    {
    $StartTime = ($TimeLines[$Index - 1] -split '::')[-1]
    $Step, $EndTime = $TimeLines[$Index] -split '::'

    '{0} took {1} [hh:mm:ss] to complete.' -f $Step, ([timespan]$EndTime - [timespan]$StartTime)
    }

output ...

[Step2] took 00:02:00 [hh:mm:ss] to complete.
[Step3] took 00:03:03 [hh:mm:ss] to complete.
[Step4] took 00:02:55 [hh:mm:ss] to complete.
[Step5] took 00:12:03 [hh:mm:ss] to complete.

3 Comments

@Joseph - you are most welcome! [grin] the solution by Theo seems more robust ... i'm glad that you Accepted it ...
I have the content stored in a variable, $TestFile... with a file path of c:\windows\testfile.txt. How would your script need modified to address simply calling the variable instead of fake reading a text file?
as i mentioned in the notes, replace the entire "fake" block with a call to Get-Content to load the file into the $InStuff variable. plus, you can rename the $Var to whatever suits you. i use $InStuff as my default "no better name" variable name ... [grin]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.