Over the summer I wrote an article about Batch scripting and just a few weeks ago wrote a follow up on Bash scripting. Today I'm exploring PowerShell scripting on Windows. I've used PowerShell at work recently for automating the deployment of .NET applications. The rest of this article looks at basic features of PowerShell and how it compares to Bash and Batch.
Powershell is a command line shell and scripting language first released for Windows in 20061. It was created as a better way to perform command line scripting on Windows. Before Powershell, Batch files were used for command line scripting in the
cmd.exe CLI. While Batch was suitable for basic tasks, the scripting language was rough around the edges and lacked modern functionality. PowerShell aimed to improve upon these shortcomings.
PowerShell scripts are files with the .ps1 extension. The PowerShell language is dynamically typed and contains basic programming constructs familiar to software developers2. Powershell types are objects, and type definitions can be explicitly or implicitly declared on variables3. This is much different than Bash, which doesn't have types. In Bash everything is plain text which is interpreted differently depending on the context.
This code declares a single parameter for the basics.ps1 script. It creates a new variable
$author of type string (
[string]). If no argument is supplied to basics.ps1, the default value
"Andrew Jarombek" is used.
PowerShell scripts are easily invoked in the PowerShell CLI by writing the path of the script followed by any arguments.
Variables are written to stdout with the
Write-Host command, which is admittedly a bit more verbose than its
echo bash counterpart. The verbosity of commands is a common PowerShell gripe from developers accustomed to using Bash. The following command prints out the
One complicated aspect of PowerShell is its execution policy. Execution policies configure requirements that must be met for a PowerShell script to execute4. By default, the execution policy for PowerShell on Windows machines is
Restricted, meaning no scripts are allowed to execute. When I first started using PowerShell this was quite confusing since all my scripts failed before executing.
While you can create complex execution policy hierarchies at the process, user, and computer levels (among others), the most basic way to change the execution policy is with the
Set-ExecutionPolicy <policy> command. For example, the following command changes the execution policy to
RemoteSigned, which provides some protection against running untrustworthy downloaded scripts.
You can also check the current execution policy with
Get-ExecutionPolicy. All the different execution policies are explained on Microsoft's website.
As I previously mentioned, all types in PowerShell are objects. Because of this, variables have methods and properties that we can invoke and access. For example, the
string type has methods such as
I also utilized a PowerShell operator
-match to match a regular expression to the
If you are coming from Batch or Bash, the fact that PowerShell has objects similar to an Object-oriented programming language is surprising. PowerShell objects are very helpful in creating concise scripts and integrate well with IDEs.
Because PowerShell has object types, performing arithmetic is simpler than Bash (and much simpler than Batch). PowerShell doesn't need an integer context to perform operations like simple addition.
$age is implicitly typed in this example. It can be explicitly typed as
[int]$age = 23.
PowerShells object based type system really shines when dealing with more complex types. For example, the following script handles the
This code snippet also shows off some modern PowerShell features such as string interpolation.
Powershell arrays are also objects that are easy to work with.
PowerShell has pretty clean syntax for writing functions. By default variables defined in functions are scoped locally to the function and don't leak into the global scope. This is the opposite of Bash which leaks variables globally by default.
To create a global variable from a function, the
global: variable prefix is used. PowerShell also provides a
script: variable prefix which persists the variable across function invocations.
Function parameters and return values are a bit funky in PowerShell. Parameters are defined similar to script parameters with a
param() block. Return values are captured at the end of a function or when a
return keyword is encountered. Therefore a
return keyword isn't needed for functions to return a value! They simply declare a function exit point5.
I think the easiest way to demonstrate this behavior is with an example. The following three functions multiply a string a number of times and return a new string. They all behave the same way despite the
return keyword used differently (or missing entirely) in each.
Besides for return values, PowerShell functions behave as you likely expect.
One thing that fascinates me is the different design decisions across programming languages. I really enjoyed comparing the biggest three scripting languages - Bash, PowerShell, and Batch. I plan to use all three throughout the rest of my career, and will write more discovery posts about them as I learn more. All the code from this article is available on GitHub.