Table of Contents
- Introduction
- Executive Summary
- Learn About Cmdlets
- Learn About Return Objects' Data Types and Data Members
- Pipeline Flow - Object Oriented
- Pipeline Flow - Text
- Conclusion
INTRODUCTION
In my opinion, the most important thing about learning PowerShell, scripting or programming language, or anything at all, is to start with a good foundation, or you'll waste a lot of time.
PowerShell is a rocket ship. Can you just get into a rocket ship and learn as you go? Maybe, but there's a very, very high chance that you will crash and burn. Learn how to operate the ship before you attempt to fly.
This lesson does not delve deep into the features and capabilities of PowerShell. This lesson will teach you everything you need to know to get this rocket ship off the ground. You can take it to the next country, planet, or galaxy. That part is up to you.
PowerShell is different from other shells, as it is object oriented. Old shells process, parse, and produce text. PowerShell processes objects or text, parses text, and produces objects or text.
Let's compare the difference between these two ideas:
- PowerShell scripts (.ps1 files) with cmdlets
- Batch scripts (.bat or .cmd files) with commands
The old command prompt and batch scripts work this way:
You use command line programs that typically end with .exe
. These are not commands, they are compiled programs. Try entering cd.exe /?
or whoami.exe /?
on the old command prompt. As you can see, the question mark argument /?
is available in almost all command line programs, or "commands," in the old Windows shell (Command Prompt).
In PowerShell, Cmdlets are like the old command line programs. They are not functions or commands. They are actually programs, usually written in C# following strict specifications, to work intuitively and as expected in PowerShell.
EXECUTIVE SUMMARY
To learn the basics of PowerShell, you need to know four basic things:
First, you need to know how to help yourself learn. PowerShell provides three cmdlets that you will use consistently during your experience with the language: Get-Help
, Get-Command
, and Get-Alias
. You will always use these cmdlets as long as you're reading or writing PowerShell.
Second, you will need to realize that learning about and examining cmdlets, and learning about and examining cmdlet output, are two entirely separate things. This is very, very easy to mix up. If you can keep these two ideas separate, you'll be able to learn the language much more quickly.
You'll use System.Object's .GetType()
method and the Get-Member
cmdlet to inspect cmdlet return values. In addition to the help based cmdlets in the previous paragraph, you'll always be using .GetType()
and Get-Member
as well.
Third, you're going to need to learn a very simple concept called the "pipeline." It's as simple as saying that one cmdlet can send its output to the next cmdlet. Then, that cmdlet can send its output to another cmdlet, and on and on, as long as you need it to go on.
The fourth and final concept needed to Master the Basics of PowerShell, is to know the difference between processing text output and processing object oriented output. Old shells like CMD.EXE, BASH, and Cisco's IOS shell, only handle text based output, whereas PowerShell has the additional ability to handle object oriented output. If you understand how this handling of object oriented data is different than handling text based data, you'll understand the most fundamental way that PowerShell is different from other shells.
LEARN ABOUT CMDLETS
The first cmdlets you should know in PowerShell are Get-Help
, Get-Command
, and Get-Alias
. If you ever have questions about any cmdlet in PowerShell, you can search online on MSDN, TechNet, or Microsoft Docs for help. You can also just use PowerShell.
Get-Help
Type Get-Help
into PowerShell to read about how Get-Help works:
Get-HelpIf you want help on a specific cmdlet like
Test-Connection
or Get-Help
itself, you can use this syntax:
Get-Help Get-Helpor
Get-Help Test-ConnectionIf you use the
-Online
parameter, PowerShell will open the Web page for that help file.
Get-Help Test-Connection -OnlineThe same information you'll online should also be in PowerShell, but if you'd like to view it in a Web browser instead of a console window, the
-Online
parameter is for you. Now, if you want to get details on all of Test-Connection
's parameters, it looks like this:
Get-Help Test-Connection -Parameter *or one of its parameters:
Get-Help Test-Connection -Parameter CountMost cmdlets' help information comes with examples. Use the
-Examples
parameter to see them.
Get-Help Test-Connection -ExamplesFinally, if
Get-Help
doesn't seem to provide much information, it may need to be updated. If so, run PowerShell as Administrator and use this cmdlet:
Update-Help
Get-Command
To see a list of cmdlets, aliases, or functions, you should use the cmdlet Get-Command
. Here are some examples:
Get-Commandor
Get-Command get-*or
Get-Command *hash*
Get-Alias
Another extremely useful thing to know, is how to learn about aliases. Use Get-Alias
to enter a command and see its aliases, or to enter an alias to get its corresponding command (definition).
Get-Alias gal Get-Alias -Definition Get-AliasDo you have any experience with Windows Command Prompt CMD.EXE or Unix/Gnu/Linux BASH? The designers of PowerShell implemented aliases for cmdlets. This made it so that people from those backgrounds could use the same syntax they used in their old shells, for many PowerShell equivalent commands. For example, the commands
DIR
in Windows or ls
in *nix, show you the contents of your current directory. In PowerShell, DIR
and ls
are both aliases for the Get-ChildItem
cmdlet. Get-ChildItem
also has another alias, gci
. Let's inspect this in PowerShell:
gal -definition set-location gal -definition get-childitem gal ls gal dir gal clear gal cls gal man
Another note on the help commands in PowerShell
Note: If you're unfamiliar with the *nix man
command, skip past this section.
One more interesting note about getting help with cmdlets in PowerShell, is that there is a cmdlet named Get-Help,
a function help,
and an alias man
. It would seem that Get-Help
is the cmdlet, while help
and man
should be aliases, but this is not the case. Do this:
Get-Command Get-Help,Help,manIt seems that while
Get-Help
is a cmdlet (compiled executable program), help
is a function (a procedure with a return value, which likely utilizes the Get-Help
cmdlet in its definition - wherever that may be defined [I don't know]). Here man
is not an alias to the Get-Help
cmdlet, but for the help
function. I think it is done this way because Get-Help
does not paginate (wait for you to scroll), while help
does. I believe man
is an alias to help
because the man
command in *nix does paginate by default.
Next lets focus on the output of a cmdlet instead of the cmdlet itself.
LEARN ABOUT RETURN OBJECTS' DATA TYPES AND DATA MEMBERS
So, now that you know how to use cmdlets and get help on them, you need to be able to analyze the information those cmdlets provide. The way most cmdlets work is that they return information. This information is mostly in the form of objects, and usually not just text. These objects have a data type or class. If you're familiar with programming, you know that classes have data members. Data members are things that belong to objects, which are usually properties or methods. Properties are pieces of information about the object's state, and methods are the actions that these objects may perform.
In simple terms, getting help with cmdlets and getting help with cmdlet output is different, and this is easily confused by beginners. First, we'll use the -Full
parameter on Get-Help
to find the output type of the Get-Date
cmdlet. Cmdlets return return objects, a.k.a. return values. These returned objects are described and defined by their type of data, a.k.a. data type, a.k.a. class. When I say "defined by," I'm referring to data members, a.k.a. properties and methods, where properties are data that describe the object's state, and methods are actions that objects can perform.
Get-Help Get-Date -Full | moreNow scroll to the OUTPUTS section of the help page. You'll see that the outputs for the
Get-Date
cmdlet are either System.DateTime or System.String, based on how you use the command. Briefly, go ahead and click the links for the DateTime and/or String data type. On the left-hand side of these Web pages, you can see the Properties, Methods, and other data members of these .Net Library data types.
Another way to get this same information is to capture the Get-Date output into a variable like this:
$_outputFromGetDate = Get-DateNow, the output from Get-Date is stored in the variable $_outputFromGetDate. Variables in programming are very similar to mathematics. If you say a=1 and b=1 then a+b=2 but PowerShell variables can hold much more than simple numbers. In PowerShell, variables always begin with
$
. Let's check what datatype this information has like this:
$_outputFromGetDate.GetType()Now to more quickly and easily get the data members with the
Get-Member
cmdlet:
$_outputFromGetDate | Get-MemberActually, you don't even have to store the output into a variable. Check this out even quicker/simpler:
(Get-Date).GetType() Get-Date | Get-MemberNow, one last comment on this last way to analyze PowerShell output; many times the output from a cmdlet will not be a single object but an array or other collection type of output. Here's how to deal with that. Let's take a look at the output from
Get-WmiObject
.
Get-WmiObject -Class Win32_LogicalDiskYou'll likely notice multiple objects are seen on the screen. One for each logical drive in Windows. Now If we check the data type, we see array.
(Get-WmiObject -Class Win32_LogicalDisk).GetType()Well, that's not helpful. So lets simply reference an index of that array and get the type of that object like this:
(Get-WmiObject -Class Win32_LogicalDisk)[0].GetType()Okay, that's better. We're looking at the ManagementObject datatype. If all the indices in the array have the same type you can just use
Get-Member
as usual:
Get-Get-WmiObject -Class Win32_LogicalDisk | Get-MemberIf types are different in the array, just reference the index you wish to analyze. Now that you understand that getting help for cmdlet output is different than getting help for the cmdlet itself, you should be able to use this information on anything and everything you encounter in PowerShell. Next let's look at how to navigate the PowerShell pipeline for processing objects vs processing text in PowerShell.
OBJECT ORIENTED PIPELINE FLOW
Okay, now let's look at typical PowerShell pipeline flow by analyzing data on a typical cmdlet's return object passed down a pipeline. I'm just going to tell you right now that the pipeline flow (I made up the term "pipeline flow") is:
CmdletVerb-CmdletNoun | Select-Object | Where-Object | Sort-Object | Format-ObjectYou probably don't know what that means right now but after I explain it below, you'll get it. Lets use
Get-WmiObject
again but let's use the alias gwmi
so we don't have to type as much.
gwmi win32_logicaldiskWhen we get data back from a cmdlet, it's usually an array or collection. We can think of this as a database table (if you're unfamiliar with that, it's like an well structured spreadsheet). What we see on the screen is each object separated by a blank line. This is not string output, these are object's properties. So like in SQL if you want to SELECT * FROM TABLENAME (select all columns in the table), let's make sure we're getting all properties from
gwmi win32_logicaldisk
by using the Select-Object
cmdlet (alias is select
).
gwmi win32_logicaldisk | select *As you can see, by default PowerShell doesn't always give you all the properties of an object when you use a cmdlet. Let’s specify which properties (or columns like in SQL) we want as in "SELECT DeviceId,FreeSpace,Size from TABLENAME" would do for us in SQL:
gwmi win32_logicaldisk | select DeviceId,FreeSpace,SizeOk now let's not get every single object, let's single out the C:\ drive. In SQL it would look something like this "SELECT * FROM TABLENAME WHERE DeviceId LIKE "%C%". To do this, we use
Where-Object
(aliases are where
or ?
). Note that we need to use the special builtin automatic variable called $PSItem
(same as $_
). This variable contains current return object(s) from the command pipeline. You can learn more about $PSItem
/$_
with this command (don't read it now, read it some other time):
man about_Automatic_VariablesHere's an example of
Where-Object
(using the ?
character as an alias):
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -like "*c*"}Did you notice the
-like
comparison operator? It's not a parameter it's an comparison operator. This comparison operator can be used with the asterisk as a wildcard. If you like to use regular expressions (I highly recommend using them) you can use the -match
comparison operator instead. Here's how to learn more about comparison operators and regular expressions:
man about_Comparison_Operators man about_Regular_ExpressionsSo lets actually single out both the c drive and the h drive like this:
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -like "*c*" -or $_.DeviceId -like "*h*"}or
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -match "[ch]"}Ok now lets move onto the next step in the pipeline flow. Lets sort our output with
Sort-Object
(alias sort
):
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -match "[a-e]"} | sort FreeSpaceor
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -match "[a-e]"} | sort DeviceId -descendingAlright, the last step of the pipeline flow when working with cmdlet output as object oriented data is to format the data. For the most part, you'll want to either see the data as a table or as a list using the
Format-Table
(ft
) or Format-List
(fl
) cmdlets like this:
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -match "[a-e]"} | sort DeviceId -descending | flor
gwmi win32_logicaldisk | select DeviceId,FreeSpace,Size | ? {$_.DeviceId -match "[a-e]"} | sort DeviceId -descending | ftUp until this point, that's all you really need to ever know to use PowerShell as a basic user. I'll show you how to process output as text though just because it's nice to know.
TEXT-ORIENTED PIPELINE FLOW
It's almost always better to work with object oriented data but here's how to work with text if you need to. You can always use the old FINDSTR.EXE
utility from the old Command Prompt days in PowerShell. But PowerShell provides Select-String
(sls
) as a native cmdlet specifically used to work with PowerShell.
So to use FINDSTR.EXE
, you have to use the /?
argument because it's an old shell command, not a cmdlet so let's see it:
FINDSTR /?Anyway, let's use
FINDSTR
on our logical disk output.
gwmi win32_logicaldisk | FINDSTR /I "c:"This is not helpful because it's formatted as a list. Let's format it as a table:
gwmi win32_logicaldisk | ft | FINDSTR /I "c:"That's better, but what are the fields? Let's expand our regex (very limited support within
FINDSTR.EXE
):
gwmi win32_logicaldisk | ft | FINDSTR /I "deviceid c: h:"Okay now lets use
Out-String
and Select-String
. Out-String converts cmdlet output from object oriented data to text data. Select-String can parse strings (not objects) for text we're looking for using regex by default or simple search using the -SimpleMatch
parameter. SimpleMatch will not treat your string as a regex. Here we go:
gwmi win32_logicaldisk | ft | Out-String | Select-String "c:"What the heck?
Select-String
didn't select a specific line. Why? Because Out-String
converts the entire output of a cmdlet into one big large string with line separators intact. What we want is an array of strings split upon a newline character. To do that, lets use the -Stream
parameter on Out-String like this:
gwmi win32_logicaldisk | ft | Out-String -Stream | Select-String "c:"Ok cool that works as expected. Now here are some other examples:
gwmi win32_logicaldisk | ft | Out-String -Stream | Select-String -SimpleMatch "c:" # This doesn't see "c:" as a regex gwmi win32_logicaldisk | Out-String -Stream | Select-String 'device|free|size' # Notice, no Format-Table (ft) gwmi win32_logicaldisk | ft | Out-String -Stream | Select-String "device|[ch]:"
CONCLUSION
Many times, it will be easier to process objects instead of parsing text. Also, if you're storing your cmdlet output into a variable, you can only utilize properties and methods of the System.String data type on text based output whereas you can use more meaningful properties and methods of different datatypes when you just process the data using the object-oriented way. With this knowledge, you can learn about or read almost any PowerShell code you come across. After reading this, you likely will not have a complete understanding of PowerShell but you do now have a very solid foundation. If you want to learn more, I would first consult technet or msdn. Otherwise there are tons and tons of unofficial but extremely helpful examples, guides, and tutorials online.
What I recommend that you do next is check out the Microsoft Docs page for PowerShell for deeper learning. Here is a page that expands upon this guide. Here's Microsoft's official page for PowerShell example scripts and tutorials. And finally, here's the first page for the reference on the entire PowerShell language.
Note: at the top of those pages, you can change the version of PowerShell to whatever you're working with.
No comments:
Post a Comment