Getting Started - Command Discovery

Welcome to my Getting Started with Windows PowerShell series!

Terminology Overview

This part may not be as exciting as the hands on stuff, but it is important to understand. Don't worry if you don't grasp a lot of it right away. It will all come together the more you use PowerShell. When your knowledge of it deepens you'll see more and more use cases for it yourself! We'll actually be building scripts, functions, and modules in subsequent posts! If you would like to skip to the hands-on stuff (and I don't blame you!) click here.

Cmdlets

Cmdlets are essentially snippets of .NET code or classes that perform a single function in PowerShell. The core functionality of PowerShell revolves around Cmdlets. Keep in mind that when you are creating functions, modules, or scripts that you aren't actually creating Cmdlets. If you'd like to look further into actually creating Cmdlets, check out this page. Please note that it is not actually writing PowerShell at that point.  It's .NET code that will interact, and provide functionality TO PowerShell.

Scripts

A PowerShell script is simply a file with a .ps1 extension that contains a series of PowerShell commands. The profile we created is actually an example of a PowerShell script.

Functions

A function in PowerShell is a series of commands that are grouped together to perform a specific task (or at least, that's the best practice). You can write functions and scripts that mimic the behavior of Cmdlets (via providing error checking and comment based help). Here's an example of a function I wrote in my post about using the LIFX API.

function Set-LightState { #Begin function Set-LightState
    [cmdletbinding()]
    param(
        [Parameter()]
        [ValidateSet('on','off')]
        [string]
        $state,
        [Parameter()]  
        [string]
        $color,
        [Parameter()]
        [ValidateRange(0.0,1)]
        [double]
        $brightness,
        [string]
        $selector
    )

    if ($lifxLight.Connected) {          

        $fullURL  = "$baseurl/$($lifxLight.id)/state"             
        $payload  = [PSCustomObject]@{

            power      = $state
            color      = $color
            brightness = $brightness

        }

        $payloadJson = $payload | ConvertTo-Json

        Write-Host "Attempting to Change light to:" -ForegroundColor $foregroundcolor
        Write-Host `t"State     :" $state
        Write-Host `t"Color     :" $color
        Write-Host `t"Brightness:" ($brightness * 100)"%" `n

        $stateReturn = Invoke-RestMethod -headers $headers -uri $fullURL -Method Put -Body $payloadJson -ContentType $acceptheader
    
        Write-Host "API status:" -ForegroundColor $foregroundcolor
        Write-Host `t"Light :" $stateReturn.results.label
        Write-Host `t"Status:" $stateReturn.results.status `n
    
    } else {
        
        $lightName = $lifxLight.label
        Write-Host "$lightName is offline or unreachable :("
        
    }    

} #End function Set-LightState

Here are some examples of that function in action.

Below you can see it being referenced in some code further on in the script.

Let's create a simple function to see how they work interactively.

function Get-RandomNameChaos {
param([string]$Name,
      [int]$Times
     )
    
    if ($Name) {
        $i = 1
        
        Do {

            Write-Host (Get-Random "`t","`n") [$i] [$Name] -foregroundColor (Get-Random -Minimum 1 -Maximum 15) -backgroundColor (Get-Random -Minimum 1 -Maximum 15)
        
            $i++
    
        } While ($i -lt ($times + 1)) 

    } else {

    Write-Host 'No name specified!' 
    
    }
}

This functions takes two arguments, -Name and -Time

You can get the function to work in PowerShell by:

  • Copy and pasting it in from above
  • Putting it in a script file and calling it within the script
  • Putting it in a module file and importing the module

I decided to paste it in and show you what happens as an example. I ran:

Get-RandomNameChaos -Name Mike -Times 3

Functions can take different arguments which are assigned to the parameters defined. 

This function we created has [int] next to the $Times parameter. This means that if you put anything other than an integer as a value, you will get an error message. 

Try running the function by calling it with different arguments. Maybe 100 for times, or even putting a non-integer value in just to see what it does.

Modules

Modules are PowerShell scripts with a .psm1 extension that contain a group of functions. Once you write a lot of functions you use over and over, it's generally a good time to create a module. You can import modules in PowerShell by using the Import-Module command. Here's an example of a logging module I created:

Above I imported my module to add logging functionality via specific functions I created. I then ran Get-Command -Module ninjalogging to get a list of commands it provides. 

Command structure

PowerShell Cmdlets (and well written functions) utilize the following format: Verb-Noun.

You can use the Get-Verb command to see a list of verbs that PowerShell uses and are officially approved. This can be handy when you're creating your own functions so you can use an approved verb that fits.

You can also run the following command which utilizes Get-CommandGroup-Object, and Sort-Object to list out the most commonly used verbs for all the PowerShell CMDlets you have access to.

Get-Command -CommandType Cmdlet | Group-Object Verb | Sort-Object Count -Descending | Format-Table -Autosize

If you have any questions about the above string of piped commands let me know in the comments, or try using Get-Help <command>.

As you can see a lot of the Cmdlets use only a few of the approved verbs. I find myself trying to use a lot of those verbs myself. There's Get, Set, and New just to name a few.

Aliases

There are aliases in PowerShell that make writing out one-liners and troubleshooting during a fire easier to manage. To see a list of aliases type Get-Alias.

You can even create you own aliases and use them in PowerShell. The alias I personally use the most is ls. To see what that Alias is linked to you can use:

Get-Alias -Name ls

ls is simply an alias for Get-ChildItem

Simple, but it provides a quick way to execute different Cmdlets and shorthand format.

You can check if an alias exists, and create your own with the following commands:

Get-Alias -name log
New-Alias -Name log -Value Out-LogFile -Description 'Link to Out-LogFile'
Get-Process | Out-String | log

The above example will not work for you as you do not have a function named Out-LogFile. What it does in my case is creates a text file logging the output of Get-Process. If you want to try to create your own alias, use Get-Process for the value and psax for the Name. Give it any Description you'd like.

You can then use:

Get-Alias -Name 'your alias name' 

to check on it. Here is the result for my example:

Command Discovery

The most straightforward command to use to discover commands is... Get-Command.

This command will display the commands as well as the command type, version, and its source.

You can narrow the scope of this command in various ways. One way is to specify the source:

Get-Command -Module Microsoft.PowerShell.Management

Let's say you wanted to discover all the commands available that had to do with processes. Let's try:

Get-Command -Name "*process*"

You could also run:

Get-Command | Where-Object {$_.Name -like "*process*"}

The $_. placeholder variable stands for the current element in the pipeline. In this case it would run through each line that is returned from Get-Command and look at the property Name ($_.Name). It then uses the comparison operator -like which allows us to specify wildcards. 

Either way the results are the same, and let's focus in on...

Get-Process

Here is the result of running Get-Process.

Formatting the results

You can also pipe the command to Format-List

Get-Process | Format-List

Here's an example of how to use Select-Object and Sort-Object. You can add more properties the the list if you want! Mess around with it to see what you can come up with that's useful to you. Here's what I ran:

Get-Process | Select-Object Name,CPU | Sort-Object CPU -Descending | Format-Table -AutoSize

Another way to output this information is to use Out-GridView. This can be a great way to get a visual of the returned items.

Get-Process | Out-GridView

In this instance I know that Get-Process is actually capable of displaying much more information. Let's try this command:

Get-Process | Select-Object * | Out-GridView

Well, that's a lot more information!

You can even add criteria to narrow it down.

I added one here (Use the [+ Add Criteria] button in the upper left) for VirtualMemorySize that islessthanorequalto 100000000.

Here's another one for Name contains chrome.

Exporting the Results

We can also use Get-Process with > and >>. These are very simple ways to output the results to a file.

Creates a new file every time (and will overwrite the file if it exists)
>> Appends the results to a file if it exists, or creates one if it doesn't

Let's look at:

Get-Process > processes.txt

Let's run the following command to see the contents of the file:

Start-Process .\processes.txt.

You can also use [TAB] completion. Try typing Start-Pro[TAB] .\pr[TAB]

Start-Process will take the input you give it and then run that with the associated application handler that's set. For example:

Start-Process www.google.com

The above command would open your default browser, and then navigate to www.google.com

Back to Get-Process!

If you were to use:

Get-Process >> processes.txt

It would append the results to the file that already exists.

Out-File

You can also use Out-File. I prefer this option as you have more control over what happens. You can specify the encoding, path, and other various options such as NoClobber.

Let's give it a try:

Get-Process | Out-File -FilePath C:\ninja\process.txt 

To append to this file using Out-File you would use:

Get-Process | Out-File -FilePath C:\ninja\process.txt -Append

Using Stop-Process with Get-Process

One of the handy things you can do with Get-Process is pipe it to Stop-Process. Just don't do this without specifying a process name, or you will very likely end up causing your computer to reboot!

This can, however, be a handy way to kill a hung process. Let's say you wanted to close any open notepad.exe instances. 

Get-Process notepad | Stop-Process

You wont see any information returned, and that usually means it found the process and stopped it as well. 

Profile Review

Below I have commented out the profile we created in part one with information on what each line is doing. 

#Set the variable $foregroundColor to be white. Global means it will be set in all scopes. 
$global:foregroundColor = 'white'

#Store the results of Get-Date in the $time variable
$time = Get-Date

#Store the current version in the $psVersion variable
$psVersion= $host.Version.Major

#Store the current user and computer in each variable 
$curUser= (Get-ChildItem Env:\USERNAME).Value
$curComp= (Get-ChildItem Env:\COMPUTERNAME).Value

#Write-Host writes to the host that's running the script. In this case, very likley your console!
#The following lines takes the variables above and uses them in the output in various ways.
Write-Host "Greetings, $curUser!" -foregroundColor $foregroundColor
Write-Host "It is: $($time.ToLongDateString())"
Write-Host "You're running PowerShell version: $psVersion" -foregroundColor Green
Write-Host "Your computer name is: $curComp" -foregroundColor Green
Write-Host "Happy scripting!" `n

#This replaces the default prompt function with our own!
function Prompt {

#We get the time again as this function runs every single time you hit enter.
$curtime = Get-Date

#Writing to the host, this time with -noNewLine.
#-NoNewLine means it will keep writing to the same line. 
#This allows us to use multiple colors on the same line.
Write-Host -NoNewLine "p" -foregroundColor $foregroundColor
Write-Host -NoNewLine "$" -foregroundColor Green
Write-Host -NoNewLine "[" -foregroundColor Yellow

#This line uses a string format operator (-f) and takes the results from Get-Date and formats them on the left.
#I'll be detailing more about how that works in a later post!

Write-Host -NoNewLine ("{0:HH}:{0:mm}:{0:ss}" -f (Get-Date)) -foregroundColor $foregroundColor

#Closing off the prompt 
Write-Host -NoNewLine "]" -foregroundColor Yellow
Write-Host -NoNewLine ">" -foregroundColor Red

#This variable controls the PowerShell window title. We set it to the current path everytime this function runs.
$host.UI.RawUI.WindowTitle = "PS >> User: $curUser >> Current DIR: $((Get-Location).Path)"

#This next line is needed so we can override the default prompt with our Write-Host above!
Return " "

}

Homework

  • Write your own profile using the one I gave you as a template for ideas.
  • Customize the Get-RandomNameChaos function some more!
  • Figure out just what the heck "{0}" -f (Get-Date) does and how it works. (Hint: String format operators)
  • Get Get-Process | Out-GridView to show you only properties you care about.
  • Learn what a calculated property is.

In part 3, we go over the ISE and creating functions!

-Ginger Ninja

[Back to top]