Scripting with Windows PowerShell Part 2
Posted by davidnewcomb on 03 Jun 2011 in PowerShell
Sorting, grouping, and formatting output
Part 2 of a 5 part series.
There is no difference between running PowerShell commands on the command line or running them in a script, so it’s more like a Unix shell. PowerShell cmdlets have a 2 part name: a verb followed by a noun. For example, Get-Process, Get-Service, Get-EventLog, Set-Service, Start-Process, Add-Content. We can get a list of all the verbs using:
Get-Verb
Verb Group ---- ----- Add Common Clear Common New Common Wait Lifecycle Debug Diagnostic ...If you stick to these “approved” verbs, it will make it easier for you to find (or guess) the command you are looking for. Let’s say we want to create a new file. We can see there is a New verb and so we can use the Get-Command command to find other commands that start with the New command verb.
Get-Command -Verb new
CommandType Name Definition ----------- ---- ---------- Cmdlet New-Alias New-Alias [-Name] <String> [-Value] <String> [-D... Cmdlet New-Event New-Event [-SourceIdentifier] <String> [[-Sender... Cmdlet New-EventLog New-EventLog [-LogName] <String> [-Source] <Stri... ...We can see that we can use Windows PowerShell to create a new event log and new events. We can have our own personal event log or an event log shared by all our scripts. The last article Scripting with Windows PowerShell Part 1 looked at the Get-Process command that lists some common attributes of a running process, however the list of columns is cut down. There is loads more information available from the command. We can use Format-List to retrieve it. Launch notepad so we have something to play with. Do a Get-Process notep* so we can find it’s PID in case we have more than one notepad loaded.
Get-Process -id 4180 | Format-List *
__NounName : Process Name : Notepad2 Handles : 69 VM : 77967360 WS : 7745536 PM : 1986560 NPM : 7680 Path : C:\Program Files\Notepad2\Notepad2.exe NonpagedSystemMemorySize : 7680 NonpagedSystemMemorySize64 : 7680 PagedMemorySize64 : 1986560 PagedSystemMemorySize : 159088 StartTime : 18/05/2011 17:27:27 ...We can use the -Property to just display the one we want.
Get-Process -id 4180 | Format-List -Property StartTime
StartTime : 18/05/2011 17:27:27We can display the results in a table, so let’s create a table containing the process name and the start time of each process. In the example I use “name", but “name” is an alias for “processname".
Get-Process | Format-Table -Property name,starttime
Name StartTime ---- --------- albd_server 17/05/2011 09:34:45 atieclxx 17/05/2011 09:35:04 atiesrxx 17/05/2011 09:34:40 audiodg CCC 17/05/2011 09:36:12 cccredmgr 17/05/2011 09:34:45 ...You can see that audiodg doesn’t have an associated StartTime. This is because my current user doesn’t have enough privileges to see it. We can re-run the command as a privileged user or we can filter out the entries where StartTime is blank. You should be getting the hang of this now. The Scripting Guy (Ed Wilson) gave a couple of waffley excuses why the where couldn’t go at the end. The long and the short of it is that once text/records/objects have flowed through a pipe into a formatter they no longer have their object association and are in fact just text (like in a traditional unix pipe). To that end the where must go before any formatting has corrupted the data. Another change is that we will be treating the records created by the Get-Process as real objects and so we need the where-object condition statement instead of a normal where.
Get-Process | where-object { $_.starttime } | Format-Table -Property name, starttimeThis is a lot of typing but there are aliases for most of the commands
gps | ? { $_.starttime } | ft name, starttimewhere, gps = (alias for) Get-Process ? = where ("where” is an alias of Where-Object) ft = Format-Table (and it knows that -Property is the magic attribute in the same way that -id is the magic attribute for Get-Process). It is recommended not to use aliases inside your scripts. All scripts should be readable and transferable which might not be the case if they are full of aliases. You can find out about aliases with:
Get-AliasIf you enter a command which is syntactically incorrect then you will get an error message describing the problem in English! Enter the broken command:
Get-Alias where-object
Get-Alias : This command cannot find a matching alias because alias with name 'where-object' do not exist. At line:1 char:10 + Get-Alias <<<< where -object + CategoryInfo : ObjectNotFound: (where-object:String) [Get-Alias], ItemNotFoundException + FullyQualifiedErrorId : ItemNotFoundException,Microsoft.PowerShell.Commands.GetAliasCommandFrom the error message we can see that the ‘where-object’ is not a valid alias. Wow, I think we have reached a new heights of waffle. It took slightly over a minute to say “read any error messages, because they will help you"! Ed spent a minute or two trying to show that ? was an alias for where, but failed because ? is a wildcard character meaning “a single character". Try it:
Get-Alias ?
CommandType Name Definition ----------- ---- ---------- Alias % ForEach-Object Alias ? Where-Object Alias h Get-History Alias r Invoke-HistoryHe (Ed) then tried Get-Alias -Name ? and Get-Alias -Name “?” to no avail. I tried a couple of other solutions to try and delimit the wildcard property of the question mark, but I couldn’t either. During the questions and answers at the end of the webcast a live listener offered the answer, which flies in the face of everything we know about command line parsing. Trust Microsoft to get themselves in a pickle then invent a way of getting out of it by destroying something else. Anyway here’s the answer:
get-alias -name [?]Eventually he showed us the less useful reciprocal command to find the aliases for a particular command:
Get-Alias -Definition where-object
CommandType Name Definition ----------- ---- ---------- Alias ? Where-Object Alias where Where-ObjectFrom Scripting with Windows PowerShell Part 1 we re-cap:
get-service | Where-Object { $_.status -eq “running” }
Status Name DisplayName ------ ---- ----------- Running Albd Atria Location Broker Running AMD External Ev... AMD External Events Utility ...We use the heading name as the identifier in the $_ line object. It would appear that the normal output you get from Get-Services isn’t the only information, like Get-Process there seems to be a lot of “hidden” information that is available. Let’s say we wish to find all the services that we can pause? We can use the Format-List (with the show-everything wild-card star) to find out about the hidden columns. You can add a “| more” to stop it scrolling off the top of the screen if you like or press CONTROL+c, to cancel the scrolling before the end.
Get-Service | Where-Object { $_.status -eq “running” } | Format-List *
Name : Albd RequiredServices : {TcpIp, RpcSs} CanPauseAndContinue : True CanShutdown : False CanStop : True DisplayName : Atria Location Broker DependentServices : {} MachineName : . ServiceName : Albd ServicesDependedOn : {TcpIp, RpcSs} ServiceHandle : SafeServiceHandle Status : Running ServiceType : Win32OwnProcess Site : Container : Name : AMD External Events Utility ...Here we can see the list of all the available attributes. We can then inspect the list and examine each CanPauseAndContinue to see which is set to true. To speed things up we can use CanPauseAndContinue as our $_ filter. Truth is implied by virtue of the fact that it is set to something (i.e. not empty):
Get-Service | Where-Object { $_.CanPauseAndContinue }
Status Name DisplayName ------ ---- ----------- Running Albd Atria Location Broker Running cccredmgr Rational Cred Manager Running LanmanServer Server ...Applying a filter is much better than visually inspecting the results by hand - thanks Ed that’s another minute of life I will never get back. We could have used Get-Member to help us select an appropriate property to filter on. (Don’t worry, we’ll look at Get-Member a bit later on). Pausing LanmanServer will prevent anyone from making new connections to the machine, but how to we pause a service? Let’s take a look at our service commands. I had a quick guess about how to do this. First, I did:
Get-Command -Name *service*
CommandType Name Definition ----------- ---- ---------- Application api-ms-win-service-core-l1-1-0.dll C:\Windows Application CcVsiLanService.dll C:\Program Cmdlet Get-Service Get-Servic ...which contained all sorts of other things so then I did a:
Get-Command -Name *service* | Where-Object { $_.commandType -eq “cmdlet” }
CommandType Name Definition ----------- ---- ---------- Cmdlet Get-Service Get-Service [[-Name] <st Cmdlet New-Service New-Service [-Name] <Str Cmdlet New-WebServiceProxy New-WebServiceProxy [-Ur Cmdlet Restart-Service Restart-Service [-Name] Cmdlet Resume-Service Resume-Service [-Name] < Cmdlet Set-Service Set-Service [-Name] <Str Cmdlet Start-Service Start-Service [-Name] <S Cmdlet Stop-Service Stop-Service [-Name] <St Cmdlet Suspend-Service Suspend-Service [-Name]which yielded the correct results. I failed to realise the Service is the Noun component of the PowerShell cmdlet and what I should have done was a
Get-Command -Noun *service*As you can see there are several things that might fit: Resume-Service and Suspend-Service but Ed shows us Set-Service instead because you can do it all in one command. It would seem that Resume-Service and Suspend-Service are short-cuts for Set-Service -Status enabled and Set-Service -Status disabled respectively.
Set-Service -Name lanmanserver -Status paused Get-Service -Name lanmanserver
Status Name DisplayName ------ ---- ----------- Paused lanmanserver ServerSet-Service -Name lanmanserver -Status running will resume the service. If it moans about “Access denied” then you will have to run the PowerShell as an administrator. We can use Get-Member to discover what properties and methods we can use on a particular cmdlet.
Get-Service | Get-Member
TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- Name AliasProperty Name = ServiceName RequiredServices AliasProperty RequiredServices = ServicesDependedOn Disposed Event System.EventHandler Disposed(System.Object, System.Event Close Method System.Void Close() WaitForStatus Method System.Void WaitForStatus(System.ServiceProcess.ServiceC CanPauseAndContinue Property System.Boolean CanPauseAndContinue {get;} CanShutdown Property System.Boolean CanShutdown {get;} ...We can see methods that we can run on a service. WaitForStatus looks like an interesting Method. This is similar to the Window’s Management Interface (WMI).
Get-WmiObject win32_service
ExitCode : 0 Name : AeLookupSvc ProcessId : 0 StartMode : Manual State : Stopped Status : OK ExitCode : 0 ...If we pipe the output of the Get-WmiObject into Format-List or Get-Member we can see more detailed information and available options.
Get-WmiObject win32_service | Format-List * Get-WmiObject win32_service | Get-MemberWhere Format-List will give us a list of properties and values there as Get-Member will us the prototypes and method signatures. Try them! If we wanted to set the password for a service we would have to use WMI commands instead because Set-Service doesn’t support it. Start-Process We can start programs on the command line just by running the program, but Start-Process gives us a little more control over the way in which the process starts. By typing Get-Help Start-Process we can find out that we can launch the application from a particular location, re-direct output, set the working directory, up environment variables or starting an application minimized:
Start-Process -WindowStyle Minimized -FilePath notepadIn the case of Start-Process the -FilePath is the magic command line switch so that the following commands are equivalent:
Start-Process -FilePath notepad Start-Process notepadWe have specified Minimized as the WindowStyle but if we get it wrong the error message will specify the possible enumerations for that command line option. Strangely Start-Process | Get-Member doesn’t produce any results because Start-Process has required fields. Questions and answers Q: Is there a way to start PowerShell from the run dialogue box? A: Yes, Start -> Run, then type powershell. You can get the help from a command prompt by:
powershell -?
PowerShell[.exe] [-PSConsoleFilewhere NoExit = will leave it running. Sta = Start the shell using single-threaded apartment mode. NoProfile = starts the script without running you profile. Good for testing because the destination machine won’t have the same profile as you. EncodedCommand = if characters on the command line are too complicated then you can accept them base-64 encoded. Command = you can either pass in a command, a script block or a script file. For more information you can read the rest of the help. Here are some examples of running powershell from the start menu or as part of a scheduled task: powershell same as powershell -noexit Runs up a powershell that stays open. powershell Get-Process Runs the Get-Process command then exits powershell -noexit Get-Process Runs the Get-Process command then gives you the command line. powershell &{get-process; sleep 2} Run powershell using an in-line script block that lists the processes, waits for 2 seconds then exits. powershell -file c:\file.ps1 Runs the file.ps1 script. ps1 is the given extension of powershell scripts. Q: Is there any difference between running an executable from the command line or via PowerShell’s Start-Process A: No. Another useful alias is sort which is an alias for Sort-Object. A nice example of this is:| -Version ] [-NoLogo] [-NoExit] [-Sta] [-NoProfile] [-NonInteractive] [-InputFormat {Text | XML}] [-OutputFormat {Text | XML}] [-WindowStyle
Get-Process | Sort-Object handles
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 0 0 0 24 0 0 Idle 30 2 448 1152 5 0.05 272 smss 31 4 872 2568 22 0.00 2056 conhost 55 6 1148 20752 26 0.02 1396 svchost 58 7 1176 20756 26 0.02 1152 svchostRemember you can use Help -Full sort to get a set of examples. Q: Can I use and equals sign “=” instead of -eq in the where clauses? A: No, equals is used as an assignment operator.
PS C:\> $d = 40 PS C:\> $d 40 PS C:\\>Q: How do I start or stop a process on a remote computer? A: Start-Process/Stop-Process only work on the local computer. You would have to use WMI or the newer PowerShell invoke-command (PowerShell v2+).
Invoke-Command -ComputerName my-host -ScriptBlock {Start-Process notepad} -Credential my-user\my-domainQ: Do you have to install PowerShell on both machines. A: Yes, unless you are talking over WMI. Q: Could you show how to use Get-Process to sort the startup time? A: Ed never answered this but here you go!
get-process | Sort-Object $_.starttime | Format-List name, starttimeQ: Can I use powershell -ExecutionPolicy to override the default policy and allow unsigned scripts to run when unsigned scripts are by default prohibited? A: Yes you can, use -ExecutionPolicy bypass Q: How do you run a command against another server with altered credentials? A: Basically any command that has the -Credential option will let you do it. Q: Can you use ADSI to stop remote services? A: Yes Q: Is there a way to encrypt the password so you do not have the privileged account in plain text? A: Yes, you can create a Credential object and use that instead. Q: Does the Invoke-Command require powershell v2 A: Yes. Q: Can you change how much history you store? A: Yes, change the variable $MaximumHistoryCount to number of entries:
$MaximumHistoryCount = 128
2 comments
Comment from: Aslesh [Visitor]

Comment from: davidnewcomb [Member]

Something like:
get-process | ? { ([DateTime]::Now - $_.StartTime).TotalSeconds -gt 7200 }Where 7200 is the number of seconds in 2 hours.
Form is loading...
Hi
could you please help me how can i get only processes running from last 2 hours.
i tried using where object, but it didnt work for me