Hey, Scripting Guy! Question

Hey, Scripting Guy! I want to do a query for a process, and if the process is running, I want to delete that process. I know I could write a script by using WMI, but is there something native to Windows PowerShell that I can use to find and to delete the process? Next question: what if there is more than one process I have to delete? Can I find those processes and delete them? I remain your humble youngling.

- RB

SpacerHey, Scripting Guy! Answer

Hi RB,

Yes, the force is strong with this one. We can sense it by the calm aura that pervades the manner of one who possesses such strength. Such power does not have to be flashy, as is exhibited by this creature:

Image of a shark off Little Cayman

 

Windows PowerShell is not flashy, but do not mistake it for a mere replacement for the command prompt. The real power of Windows PowerShell is in the way that we can use one command to feed into another command. Mastering the pipeline unlocks the key to excellent capabilities (kind of like mastering the lightsaber is essential for younglings).

This week we will be looking at the basics of Windows PowerShell. Windows PowerShell is installed by default on Windows 7 and Windows Server 2008 R2. It is an optional installation on Windows Server 2008 and a download for Windows Vista, Windows XP, and Windows Server 2003. The Windows PowerShell Scripting Hub is a good place to get started with Windows PowerShell.

We are not sure that telling you to close your eyes and feel the pipeline would do much good. Neither would exhortations to become one with the pipeline. Therefore, we will provide some examples to show by using the Windows PowerShell pipeline. If we want to obtain information about the Notepad process (assuming that Notepad is actually running), we use the Get-Process cmdlet:

Get-Process Notepad

We do not have to specify the –name parameter if we do not want to do this, because the –name parameter is the default parameter with Get-Process. We can type the –name parameter and get information about the Notepad process. This is seen here:

PS C:\> Get-Process -name notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     47       2      976       3512    59     0.10   3960 notepad

To stop the Notepad process, we use the Stop-Process cmdlet. If, however, we are not used to using the –name parameter with the Get-Process, cmdlet we will receive a shock when we try the same syntax with Stop-Process:

PS C:\> Stop-Process notepad
Stop-Process : Cannot bind parameter 'Id'. Cannot convert value "notepad" to ty
pe "System.Int32". Error: "Input string was not in a correct format."
At line:1 char:13
+ Stop-Process <<<<  notepad
    + CategoryInfo          : InvalidArgument: (:) [Stop-Process], ParameterBi
   ndingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerSh
   ell.Commands.StopProcessCommand

The reason for the error is that the –name parameter occupies the first position for the Get-Process cmdlet, and the –id parameter is the first position parameter for the Stop-Process cmdlet. When we did not use any named parameters, the Stop-Process cmdlet looked for a process with the process ID of "notepad" which is not an integer, and this caused the error. The –name parameter is a named parameter in the Stop-Process cmdlet. This means if we want to use the name of a process to stop, we must specify the –name parameter. This is seen here:

Stop-Process -name notepad

To avoid these kinds of errors, you can always use parameters (which is a best practice when you write scripts), or you can use the pipeline. The advantage of using the pipeline is that you do not have to worry about all the parameters. You can use Windows PowerShell to find the process that you are interested in, and pipeline the results of the first command to the second command that will stop the process. This is seen here:

Get-Process notepad | Stop-Process

A session that starts an instance of Notepad, identifies the Notepad process, and then deletes that process is seen here:

Image of a Notepad process being started, identified, and deleted

 

You can use wildcard characters to identify processes. This technique can be both dangerous and useful. Here is an example of using wildcard characters to simplify finding all the Notepad processes:

PS C:\> Get-Process note*

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     47       2      976       3464    59     0.05   2056 notepad
     47       2      976       3488    59     0.09   3292 notepad

You can then pipeline the result to the Stop-Process cmdlet and stop all instances of the Notepad process that are running on the computer:

Get-Process note* | Stop-Process

An example of working with processes by using wildcard characters is seen here:

Image of working with processes by using wildcard characters

 

Using wildcard characters can be dangerous if you are not careful. An example of such a dangerous command is seen here where we get a list of all the processes that are running on the computer, and pipeline them to the Stop-Process cmdlet. This will stop every process that is running on the computer, which for most operating systems will cause the computer to shut down (on Windows Vista and later, this command would have to be run with administrative rights).

Get-Process * | Stop-Process

Of course, if you want to shutdown the operating system it is best to use the shutdown method from the Win32_OperatingSystem WMI class.

Suppose we have several instances of Notepad that are running. One instance has been running for a while and has consumed more CPU time than the other process. We can get this information as seen here:

PS C:\> Get-Process notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     47       2      976       3452    59     0.10   2688 notepad
     49       2     1160       3936    60     1.13   3984 notepad

Whereas we could definitely use the process ID—3984 in this example—to stop the process that is using the most CPU time, we may not want to type two separate commands (or perhaps we want to automate the task of stopping a process that is using too much CPU time). To do this, we pipeline the results of the first query to the Where-Object cmdlet. We can use the alias for Where-Object, which is just Where. The alias eliminates some typing that is required for this command without sacrificing any readability. If we were not worrying about readability, we could use gps as an alias for the Get-Process cmdlet, and we could use ? as the alias for the Where-Object. The short command is shown the Where-Object. The short command is shown here:

PS C:\> gps notepad | ? { $_.cpu -gt 1 }

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
47 2 1316 4080 60 1.38 2420 notepad

The way I generally type the command is to spell out Get-Process (I use tab completion to spell it out: I only have to type Get-p and then I press the TAB key.) The Where-Object cmdlet is used to filter the process objects as they come across the pipeline. Each instance of a process with the name of Notepad is returned by the Get-Process cmdlet. As the process comes across the pipeline, the $_ automatic variable represents the current process object on the pipeline. This enables us to examine the properties of the process object. We inspect the amount of CPU time that is being used by the process to see whether it exceeds 1. If it does, the filter will enable the process object to continue. In the example seen here we display basic information about the process on the console:

PS C:\> Get-Process notepad | Where { $_.cpu -gt 1 }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     49       2     1160       3936    60     1.13   3984 notepad

If we are not sure which properties are available for us to use in the Where-Object filter, we can use the Get-Member cmdlet. If we select the properties, we will eliminate the methods. This command is seen here:

PS C:\> Get-Process | Get-Member -MemberType property

However, we will also miss the instances of ScriptProperty and AliasProperty. To make sure we can find the other properties that were added by the Windows PowerShell team, we use a wildcard character in front of the MemberType property. The CPU property is one that was added by the Windows PowerShell team. It is a ScriptProperty. This is seen This is seen here:

PS C:\> Get-Process | Get-Member -MemberType *property


   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
NPM                        AliasProperty  NPM = NonpagedSystemMemorySize
PM                         AliasProperty  PM = PagedMemorySize
VM                         AliasProperty  VM = VirtualMemorySize
WS                         AliasProperty  WS = WorkingSet
__NounName                 NoteProperty   System.String __NounName=Process
BasePriority               Property       System.Int32 BasePriority {get;}
Container                  Property       System.ComponentModel.IContainer C...
EnableRaisingEvents        Property       System.Boolean EnableRaisingEvents...
ExitCode                   Property       System.Int32 ExitCode {get;}
ExitTime                   Property       System.DateTime ExitTime {get;}
Handle                     Property       System.IntPtr Handle {get;}
HandleCount                Property       System.Int32 HandleCount {get;}
HasExited                  Property       System.Boolean HasExited {get;}
Id                         Property       System.Int32 Id {get;}
MachineName                Property       System.String MachineName {get;}
MainModule                 Property       System.Diagnostics.ProcessModule M...
MainWindowHandle           Property       System.IntPtr MainWindowHandle {get;}
MainWindowTitle            Property       System.String MainWindowTitle {get;}
MaxWorkingSet              Property       System.IntPtr MaxWorkingSet {get;s...
MinWorkingSet              Property       System.IntPtr MinWorkingSet {get;s...
Modules                    Property       System.Diagnostics.ProcessModuleCo...
NonpagedSystemMemorySize   Property       System.Int32 NonpagedSystemMemoryS...
NonpagedSystemMemorySize64 Property       System.Int64 NonpagedSystemMemoryS...
PagedMemorySize            Property       System.Int32 PagedMemorySize {get;}
PagedMemorySize64          Property       System.Int64 PagedMemorySize64 {get;}
PagedSystemMemorySize      Property       System.Int32 PagedSystemMemorySize...
PagedSystemMemorySize64    Property       System.Int64 PagedSystemMemorySize...
PeakPagedMemorySize        Property       System.Int32 PeakPagedMemorySize {...
PeakPagedMemorySize64      Property       System.Int64 PeakPagedMemorySize64...
PeakVirtualMemorySize      Property       System.Int32 PeakVirtualMemorySize...
PeakVirtualMemorySize64    Property       System.Int64 PeakVirtualMemorySize...
PeakWorkingSet             Property       System.Int32 PeakWorkingSet {get;}
PeakWorkingSet64           Property       System.Int64 PeakWorkingSet64 {get;}
PriorityBoostEnabled       Property       System.Boolean PriorityBoostEnable...
PriorityClass              Property       System.Diagnostics.ProcessPriority...
PrivateMemorySize          Property       System.Int32 PrivateMemorySize {get;}
PrivateMemorySize64        Property       System.Int64 PrivateMemorySize64 {...
PrivilegedProcessorTime    Property       System.TimeSpan PrivilegedProcesso...
ProcessName                Property       System.String ProcessName {get;}
ProcessorAffinity          Property       System.IntPtr ProcessorAffinity {g...
Responding                 Property       System.Boolean Responding {get;}
SessionId                  Property       System.Int32 SessionId {get;}
Site                       Property       System.ComponentModel.ISite Site {...
StandardError              Property       System.IO.StreamReader StandardErr...
StandardInput              Property       System.IO.StreamWriter StandardInp...
StandardOutput             Property       System.IO.StreamReader StandardOut...
StartInfo                  Property       System.Diagnostics.ProcessStartInf...
StartTime                  Property       System.DateTime StartTime {get;}
SynchronizingObject        Property       System.ComponentModel.ISynchronize...
Threads                    Property       System.Diagnostics.ProcessThreadCo...
TotalProcessorTime         Property       System.TimeSpan TotalProcessorTime...
UserProcessorTime          Property       System.TimeSpan UserProcessorTime ...
VirtualMemorySize          Property       System.Int32 VirtualMemorySize {get;}
VirtualMemorySize64        Property       System.Int64 VirtualMemorySize64 {...
WorkingSet                 Property       System.Int32 WorkingSet {get;}
WorkingSet64               Property       System.Int64 WorkingSet64 {get;}
Company                    ScriptProperty System.Object Company {get=$this.M...
CPU                        ScriptProperty System.Object CPU {get=$this.Total...
Description                ScriptProperty System.Object Description {get=$th...
FileVersion                ScriptProperty System.Object FileVersion {get=$th...
Path                       ScriptProperty System.Object Path {get=$this.Main...
Product                    ScriptProperty System.Object Product {get=$this.M...
ProductVersion             ScriptProperty System.Object ProductVersion {get=...

As soon as we have the filter working correctly and we see that it is returning the results that we are interested in obtaining, we can just pipeline the resulting process object to the Stop-Process cmdlet. This is shown here:

PS C:\> Get-Process notepad | Where { $_.cpu -gt 1 } | Stop-Process

The ability to add pipelines together, by feeding the results of one pipeline into another pipeline as shown earlier, is where we obtain the real power of Windows PowerShell. It is a new concept for people who have a Windows background, but is something that people have done for years in other consoles. The big difference is that we pass objects through the pipeline, and not merely text. Join us tomorrow as we continue our Windows PowerShell Basics Week. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys