Hey, PowerShell Guy ! How Can I Get the Uptime of a Service?
And another Hey, Scripting Guy ! translation to powerShell How Can I Get the Uptime of a Service?
The PowerShell translation :
$s = gwmi win32_service -filter "name = 'spooler'"
"{0}" -f ((get-date) - ([wmi]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate))
I do output the Uptime in default formatting as I find this more clear, so it looks like this :
PoSH> $s = gwmi win32_service -filter "name = 'spooler'"
PoSH> "{0}" -f ((get-date) - ([wmi]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").Cr
eationDate))
1.22:57:49.1208030
PoSH>
You see that again this is very simple in PowerShell, just 2 lines,
Only that second line might look a bit cryptic at first look as a lot of processing happens in this single line, but as you break it apart you will see it's not as difficult as it looks, and easy to create using the "Admin Development Method" and REPL (Read, Evaluate, Print, loop)
so let's work out how I came to this
Lets start with the first line :
PoSH> gwmi win32_service -filter "name = 'spooler'"
ExitCode : 0
Name : Spooler
ProcessId : 1536
StartMode : Auto
State : Running
Status : OK
PoSH>
but as you could see in the original Article the Win32_Service does not provide to uptime
Now, ideally, we’d be done at this point: we’d simply report back the date and time that the service started. That would be nice, but, for better or worse, the Win32_Service class doesn’t include a property that tells us when the service started. So does that mean that all is lost? Yes, yes it does.
Ha-ha; sometimes we crack ourselves up. No, in the wonderful world of scripting nothing is ever lost. While it’s true that the Win32_Service class doesn’t have a property that tells us when a service was started, it does have a property that tells us the process ID (PID) of the process in which the service is running. Is that useful? You bet it: we can take that PID and use it to retrieve process information from the Win32_Process class. Is that useful? Of course it is: after all, the Win32_Process class just happens to have a property (CreationDate) that tells us when the process started.
So next we need to get the ProcessID and use that to construct a new get-WmiObject command to get back the Process, so I save the result in the variable $s, and go on :
PoSH> $s = gwmi win32_service -filter "name = 'spooler'"
PoSH> $s.ProcessId
1536
PoSH> gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'"
ProcessName : spoolsv.exe
Handles : 333
VM : 88211456
WS : 6844416
Path :
__GENUS : 2
__CLASS : Win32_Process
__SUPERCLASS : CIM_Process
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Process.Handle="1536"
__PROPERTY_COUNT : 45
__DERIVATION : {CIM_Process, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER : MowVista
__NAMESPACE : root\cimv2
__PATH : \\MowVista\root\cimv2:Win32_Process.Handle="1536"
Caption : spoolsv.exe
CommandLine :
CreationClassName : Win32_Process
CreationDate : 20070227192629.875000+060
CSCreationClassName : Win32_ComputerSystem
CSName : MowVista
Description : spoolsv.exe
ExecutablePath :
ExecutionState :
Handle : 1536
HandleCount : 333
InstallDate :
KernelModeTime : 6406250
MaximumWorkingSetSize :
MinimumWorkingSetSize :
Name : spoolsv.exe
OSCreationClassName : Win32_OperatingSystem
OSName : Microsoftr Windows VistaT Ultimate |C:\Windows|\Device\Harddisk0\Partition2
OtherOperationCount : 3473
OtherTransferCount : 14668
PageFaults : 8591
PageFileUsage : 5562368
ParentProcessId : 588
PeakPageFileUsage : 5750784
PeakVirtualSize : 90451968
PeakWorkingSetSize : 9691136
Priority : 8
PrivatePageCount : 5562368
ProcessId : 1536
QuotaNonPagedPoolUsage : 10672
QuotaPagedPoolUsage : 141784
QuotaPeakNonPagedPoolUsage : 13104
QuotaPeakPagedPoolUsage : 145104
ReadOperationCount : 26
ReadTransferCount : 2637
SessionId : 0
Status :
TerminationDate :
ThreadCount : 18
UserModeTime : 4062500
VirtualSize : 88211456
WindowsVersion : 6.0.6000
WorkingSetSize : 6844416
WriteOperationCount : 457
WriteTransferCount : 379016
PoSH>
You can see that this is about the same as the first line, only now I use the Win32_Prosess class and the ProcessId I did find in my first Command, I use the $() construct (that will execute the command inside the parenthesis and will add it to the string, like it is a variable that get's substituted) here to merge this with the rest of the filter string.
Now I only need the CreationDate, so I will only select that, this time I do not use a variable, but just used the UP key to get the former command and place it in Parenthesis, this tells PowerShell to first execute the code and then I can get the Property from the result without using a variable to store the output first.
PoSH> (gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate
20070227192629.875000+060
PoSH>
Ok, so far so good, but as this date is in WMI format so not really usefull, next task is to translate this to a normal date.
In the VbScript example the string is parsed and a date is constucted from that, in PowerShell we can also do that but there is a much simpler way, every WMI Object has some Standard ScriptMethods in PowerShell we can use for this :
PoSH> $s | gm -MemberType ScriptMethod
TypeName: System.Management.ManagementObject#root\cimv2\Win32_Service
Name MemberType Definition
---- ---------- ----------
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime ScriptMethod System.Object ConvertToDateTime();
Delete ScriptMethod System.Object Delete();
GetType ScriptMethod System.Object GetType();
Put ScriptMethod System.Object Put();
PoSH> $s.ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate)
Tuesday, February 27, 2007 7:26:29 PM
PoSH> ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate)
Tuesday, February 27, 2007 7:26:29 PM
PoSH>
You see that you can use the $s variable holding the Win32_Service for this, but as I find that a bit confusing (as ConvertToDateTime is not a Method of the Win32_Service WMI class but a Common helper Method) , so I normaly create an "Empty" WMI class for this ([WMI]'').
Now we have the Start time of the service as a DateTime object so it is readable and easy to work with.
to get the uptime we only need to get the current time with Get-Date and substract the startTime we have collected now
PoSH> get-date
Thursday, March 01, 2007 7:23:21 PM
PoSH> get-date - ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate)
Get-Date : A parameter cannot be found that matches parameter name '-'.
At line:1 char:9
+ get-date <<<< - ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDat
e)
PoSH> (get-date) - ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDate
)
Days : 1
Hours : 23
Minutes : 57
Seconds : 25
Milliseconds : 189
Ticks : 1726451897715
TotalDays : 1.99820821494792
TotalHours : 47.95699715875
TotalMinutes : 2877.419829525
TotalSeconds : 172645.1897715
TotalMilliseconds : 172645189.7715
PoSH>
you can see we need to use parentheses again to first execute the get-date Cmdlet and get a [dateTime] object and then we can substract the StartTime, otherwise the parser will think the - indicates a parameter to get-date.
We are almost ready but this is a bit to much information so let's format it a bit, as said I liked the default formatting better so I just used that but I will show first how you could use and format that also (for readability I used an extra helper variable first, as the scriptline was constructed by using the UP arrow and append extra code to the line, test it and then add the next step, while making the script, I did just add () again and did put "{}" -f in front (REPL) :
PoSH> ((get-date) - ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").CreationDat
e)).TotalMinutes
2886.58836705
PoSH> $timeSpan = (get-date) - ([WMI]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").
CreationDate)
PoSH> $timeSpan.TotalMinutes
2887.024227825
PoSH> [int]$timeSpan.TotalMinutes
2887
PoSH> "{0}" -f $timeSpan.TotalMinutes
2887.024227825
PoSH> "{0:n0}" -f $timeSpan.TotalMinutes
2,887
PoSH> "{0}" -f $timeSpan
2.00:07:01.4536695
PoSH> "{0}" -f ((get-date) - ([wmi]'').ConvertToDateTime((gwmi Win32_Process -filter "ProcessID = '$($s.ProcessId)'").Cr
eationDate))
2.00:09:28.1483820
PoSH>
you can see that you can just cast the Minutes to an Int to round it
but you can also use the Format String ( -f ) operator for this, also note that I did not use $() here as I did with the ProcessID before but -f.
Using -f is much easier here because I already have quotes in my command allready , so embedding in a string another time would give a lot of Escaping problems, this can be solved by using a variable to store result first, but using -f for this is much easier as the escaping problems are not there and I can keep on using REPL and just use the UP arrow and put some code in front.
I hope this post will show how to use the Admin Development method in PowerShell to tackle this kind of tasks, and how the Interactive use of the PowerShell console, and using the command history for REPL supports this.
As I use this kind of oneliners a lot, and you can find a lot of PowerShell oneLinerson the internet .
I hope this also shows, that if you find a onliner like this and do not understand the workings of it, how you can can break it apart in pieces and analyze and/or try them part by part, what makes it easier to follow and that way helps understanding the complete script.
Enjoy,
Greetings /\/\o\/\/