Preface
inb4…
Yeah, yeah. Laugh it up. But the harsh truth is as much as we can all preach for Linux goodness, Windows isn’t going away any time soon. So we need to make the best of it.
Some time ago, I made the thread Powershell - Useful Commands just to share some of the things I was doing with Powershell. I often come across lots of cool things that people are creating with Powershell, but I haven’t found a whole lot of good starter guides. I’m hoping this post isn’t insulting to experienced programmers and command line gurus, but not lost to novices.
Most of the material so far is fairly generic, these concepts will work in Powershell Core across supported platforms… However not all of the commands will be available or behave exactly the same depending on your system.
Table o’ Contents
- Outside Resources
- Before starting
- Commands to get started
- Script Blocks
- Command output
- Variables
- Manipulating Command Output
- Conditions and Comparisons
- Splatting, Arrays, and Creating Objects
Outside Resources
TL;DR… I wanna watch videos!
- Shane Young’s Powershell Tutorials: great starting point for those who don’t want to read everything
Documentation
- Microsoft Docs Powershell documentation: great for learning about individual components when looking to do something specific
- MSDN: great for the stuff that falls more into programming concepts and C#
Before actually getting started
There are a few things to keep in mind when using WIndows in general. A lot of the general wonky-ness that comes with using Windows through the GUI often applies just the same as in Powershell.
-
User Permissions - “Run as Administrator” and actually logging in with an Admin account are NOT always the same. Depending on the task at hand, there is added complexity while using Powershell for tasks that require escalated privileges.
-
Version numbers matter (a LOT) - Like any Microsoft Product, it doesn’t matter whether it’s the version of Windows, .NET, or Powershell. The version you’re running makes a difference. Not all commands support the same arguments or use context, just because the command you’re running throws an error on one system doesn’t mean it will throw the same error on another running a different version.
Learn by doing …there’s nothing more insulting than a “Hello World” project.
The vernacular:
For the most part, Powershell commands follow a naming convention that is referred to as Verb-Noun
. This is useful to keep in mind because the verbs that are used throughout different commands are typically a constant. Get, Remove, Start, Stop are fairly common for command names.
What commands are available on my system?
Get-Command
will spit out a huge list of ALL of the different commands that are currently available for you to run… And there’s going to be a lot of them. Fortunately, this can be narrowed down though a basic filter and/or argument.
A great argument add is -Noun. This filter the output based on the second word of a command. Get-Command -Noun Help
will output any commands where the noun is “Help”.
What about getting commands with a specific verb? This requires use of wildcards. Get-Command Get*
will output any commands that begin with “Get”.
Additionally, any executable files available on a system can be run. For Windows, this is any .exe; for Linux (running Powershell Core) this can be anything from the /bin/ directory to a custom shell script.
Can I get some help here?
Some commands can be more straightforward than others, either way it’s not a great idea to blindly run commands. That’s where the Get-Help
comes in. This command will show details and arguments for a given command.
Get-Help Get-Command
is all it takes. Though the arguments lists can sometimes be a little vague. Which is what the -Examples argument is for. Get-Help Get-Command -Examples
will show some examples for proper use of the command and its arguments.
Typically, the help pages will update automatically. But there is a chance this will need to be done manually from time to time. While there usually aren’t many changes for help pages, running Update-Help
every now and then wouldn’t hurt.
Making sense of a command’s output
Unlike DOS or BASH, most Powershell commands’ output are in the form of objects. Notice how with Test-Connection
each part of the output has a label and shown in a consistent table.
Each row in this table is an object, and each column is data (aka data member) in that object. Data members that are displayed can be changed by piping the output through Select-Object
To find what data that is available in an object, simply use the * wildcard. Keep in mind if the command you’re running will yield more than one object, it will show all of the data for each object without any apparent separation.
Variables
In Powershell, variables can be declared and defined as strictly or loosely as you could want; this leaves us free to create variables without defining them as strings, integers, ect… which is especially useful for creating variables that receive their data from a command.
A variable in Powershell is notated with a $ followed by its name, $PROFILE
and $HOME
are both examples of variables; these among others are also some of the predefined variables that are created when opening a new Powershell session (window).
Generic Variables
A new variable can be created by setting it = to a given set of data or a command. $numbers = 1234
and $folder = Get-Location
will create new variables named “numbers” and “folder” respectively. To check the contents of existing variables, use the Get-Variable
command or put the variable through the echo
command.
Alternatively, a variable can be created using the New-Variable
command. This method opens more options to be set when creating a variable, including using variables to create variables.
Keep in mind, a variable will store the OUTPUT of a command; not execute the command when the variable is used. For example:
Variables with Data Types
While Powershell is usually smart enough to manage these on its own, there are some instances where they need to be declared manually.
This technet page has a list of possible types that can be declared. Generally, a type would need to be declared when the stored data will be used in a comparison.
Both with and without the declaration of the variable’s type, most use cases will treat it the same. Generally there is no need to declare the type, and in some cases may cause problems. But when the behavior of a command or script is not what was expected declaring a type may solve the issue.
Script Blocks
While this is an over-simplified way of putting it, any commands or expressions inside { }
are executed in a block. The best way to learn about these is to look at the examples in the next to sections.
Commands in succession behave the same way they do in the rest of Powershell. A new command can be defined by adding a new line or adding a ;
before the next command.
Output Manipulation
There are a multitude of ways to go about making the output of a command or data in a variable useful for whatever it may be needed for. For this section I wrote a small function (command) to have some simple data to work with.
Here is a copy of it…
function Show-Example {$num = 0;while ($num -le 5) { $prop = [ordered]@{ ‘Id’ = $num; ‘Double’ = $num*2; ‘Triple’ = $num*3}; New-Object -TypeName psobject -Property $prop; $num++}}
If you’re inclined to try some of this out with the example data, just run the command above.
Getting a Single Data Member
Each object here has 3 data members in it. Before this can be used as a data source for other commands, this output will need to be narrowed down. The way this can be done is with the dot operator followed by the name of the member. Just be sure to put the original command in parentheses.
Now only the data for “Double” is being accessed. This output is perfect for commands that can accept multiple data entries.
I’m not sure if it’s convenient or unfortunate that the music client I was using became unresponsive while writing this part, but it was perfect for creating an example. I used Get-Process
(a Powershell cmdlet) to get the PID numbers, which was fed to kill
(a Linux utility).
Getting a Single Object
What about when a single object is needed/can be used? A single object can be obtained with the [ ] index operator.
Keep in mind the first object is always the 0th item… Because computers count from 0.
Getting a Single Object’s Data Member
Additionally, if only a specific data member of a specific object is needed, the dot operator and [ ] operator can be used in conjunction.
Usage with Variables
If a variable has a whole set of objects stored in it, the same rules apply, just drop the parentheses.
And yes, the inverse works as well.
ForEach-Object and Current Object
Making use of and manipulating the data of each object independently is an extremely important component to making output usable for any situation.
ForEach-Object
will loop through each object and execute any specified commands for each one. In conjunction with the “current item” variable $_
, the data in the current object ForEach is currently observing is specifically available without needing to access it through the index operator.
Out of habit, I use the predefined alias %
for ForEach-Object
. They do the same thing, but keep in mind it is technically an alias.
Where-Object
Whether its to not have to dig through useless nonsense or to get the appropriate output for a script, there are times when the output of a command needs to be narrowed down.
For example we have quite a number of entries in the Get-PSDrive
command.
By piping this through Where-Object
, we can exclude the drives that don’t have usable space.
Where-Object
has a predefined alias for ?
.
If and or… but…
Comparison Operators
Anyone that is familiar with comparison operations in a typical programming language will notice that some of the comparisons that Powershell can do generally don’t behave the same as what they’re use to. Anything beyond comparing numbers is generally trial and error.
Consult the link for this section for a list of the possible comparisons. Operators such as = < > =!
do NOT work.
If statements
The simplest way to put this is probably: IF the statement is TRUE, THEN DO whatever. If statements will test a comparison to determine what will be executed next.
Unlike most programming languages, Powershell will test if the data equals true by default.
However the implicit -eq $TRUE
is only so useful. Other comparison operators make if statements useful for comparing numbers, strings, and much more.
Anything following the if statement will be executed normally, but if there is anything that should be run if the statement is false, this is where an else statement would be needed.
SPL@TING, Arrays, and Creating Objects
Basic Arrays
Most of the time, Powershell will create an array without needing to define it. And in most cases it is completely unnecessary for creating one manually, or the same thing can be achieved with the Get-Content
command against a text file with a line break for each item.
For those who are familiar with arrays, these are dynamic arrays, so there is no need to define a size for the array.
An array can be created with @( )
Note that parentheses are used here, splat objects are created very similarly.
Splatting - Command Parameters
When writing a script, passing many values to different parameters can become cumbersome to manage. This can be made easier by passing a splatted hash table to given commands.
Splats are created using @{ }
Note that curly brackets are used here.
Splatting and Creating Objects
In the example above, a splat was created with two objects with properties of “Name” and “Value”. These can be used by New-Object
to convert these to properties for an object.
However, this becomes most useful when used in conjunction with variables and some sort of loop such as ForEach-Object
or while
.