PowerShell: Why You'll Never Go Back to Cmd.exe Batch Files
Once you go PowerShell, you’ll never want to go back to Cmd.exe batch files.
August 27, 2015
Scripts have a long history on the Windows OS platform. In Cmd.exe most people call them batch files, but they are scripts nonetheless. The command-line parsing rules that Cmd.exe uses mean that some commands you enter on a Cmd.exe command line behave differently than when you put the same commands in a batch file. For example, you have to double the % character of the iterator variable when using the For command in a batch file, but not when you type the For command at the command line. This is an ongoing source of confusion for those not familiar with Cmd.exe’s syntax quirks.
Unlike Cmd.exe, command-line parsing in PowerShell is consistent whether you type a particular command on the PowerShell command line or put the command in a script. In addition, PowerShell provides true functions rather than just Goto and Call branching, like in batch files. PowerShell functions, like functions in other programming languages, support returning values as a built-in feature. (Batch file subroutines don’t natively support returning values.) In addition, PowerShell provides superior parameter capabilities: Cmd.exe has only replaceable parameters (%1, %2, etc.), whereas PowerShell allows far more flexibility in parameters, which I will talk about shortly.
PowerShell Scripts and Parameters
To create a PowerShell script, you simply create a text file, with a .ps1 extension, that contains the commands you want to execute. The commands in the script can be PowerShell cmdlets, batch files, PowerShell aliases, PowerShell functions, or anything else that you could type at a PowerShell prompt as a command to execute.
Consider the batch file (Sample1.cmd) and PowerShell script (Sample1.ps1) shown in Figure 1. These two scripts are functionally identical and will produce the exact same output. Note that the batch file uses replaceable parameters (%1 and %2) to mean “the first two parameters on the script’s command line.” Rather than replaceable parameters, PowerShell uses the Param statement to define its parameters. (The line breaks after the opening parenthesis of the Param statement and before its closing parenthesis are optional; I included them to improve readability.)
Figure 1 - Functionally Equivalent Batch File and PowerShell Script
However, the Param statement does a great deal more than provide the same feature as batch file replaceable parameters. In fact, it provides a PowerShell script with the same parameter processing capabilities that are described in the about_Parameters documentation.
In the Sample1.ps1 script in Figure 1, the script is declaring two parameters named FirstName and LastName (that is, the variable names in the Param statement). When you run the script, PowerShell will populate the $FirstName variable with the first parameter from the command line, and it will populate the $LastName variable with the second parameter from the command line. This means that when we run this command:
Sample1 Ken Dyer
This will output the string “Hello, Ken Dyer” (without the quotes). As noted in the documentation, you can also explicitly include the parameter’s names when you run the script:
Sample1 -FirstName Ken -LastName Dyer
You can even reverse the order of the parameters if you want and get the same output:
Sample1 -LastName Dyer -FirstName Ken
Even this simple example shows how PowerShell’s parameter processing capabilities are far superior to replaceable parameters in batch files.
PowerShell Functions
In PowerShell, there’s no difference in the command line syntax between running a script and running a function. In fact, you can think of a script as a function encapsulated in a file (a .ps1 file, to be exact). As I showed in the previous section, all we need to run a script is to type the script file’s name (with or without the .ps1 extension) followed by its parameters. A function works the same way.
Figure 2 shows a function named Hello defined within a PowerShell script (Sample2.ps1). The Hello function contains identical code to Sample1.ps1 in Figure 1 (Callout 1). The only differences are the Function keyword and opening { character (Callout 2) and the closing } character (Callout 3).
Figure 2 - PowerShell Function Defined in a Script
Figure 2 illustrates how a function is equivalent to a script. The only differences are that the syntax for defining a function uses the Function keyword, a name for the function and the { and } characters to enclose the content of the function. A script doesn’t use these syntax elements; instead of a function name, you run the script by specifying its name. The Param statement works the same whether in a function or a script. Figure 2 also shows that a script can contain a function. You can also include functions inside other functions.
If you run Sample2.ps1 from Figure 2 at the PowerShell prompt, it will not produce any output because the script only defines the Hello function but does not run it. You could run the function in the script by adding the following line to the bottom of the script:
Hello Ken Dyer
This line of code would run the Hello function with two parameters for the -First and -Last parameters, just like the Sample1.ps1 script from Figure 1.
However, you can’t run the Hello function after the script completes because the Hello function is no longer defined. This is because a script runs in its own scope.
PowerShell Scopes
An important thing to note about functions is that you must define them before you can run them. This is because PowerShell runs all code in top-to-bottom order. If you try to run a function before you have defined it, PowerShell will give you the standard error message that it could not find the command. Another way to say this is that a function must exist in the current scope before you can run it. Scope refers to the visibility of an object (for example, a variable or a function) in PowerShell.
When you define a function, the function exists in the current scope, and the code inside a function gets a new scope. When the function ends, objects (that is, variables and other functions) defined within that function are no longer available. This is also the case with scripts: When you run a script, PowerShell creates a new scope. When the script ends, everything defined in that scope is no longer accessible. This is called falling out of scope.
Consider the example in Figure 3. We have the script Sample3.ps1, which defines a function (Hello) that sets a variable. When we run the script (Callout 1), the script only displays the “Hello” part of the string. This is because the $employeeName variable has fallen out of scope: It existed inside the Hello function, but once the function ended, the variable disappeared. Callout 2 shows how the Hello function also fell out of scope: After the script ended, the Hello function was no longer defined, so we got an error when we tried to run it.
Figure 3 - PowerShell scopes
As you can see from Figure 3, scope applies to functions, scripts (which are just functions encapsulated in a file) and variables. Further, PowerShell creates scopes automatically. (By way of comparison, batch files don’t have scopes unless you manually initiate scope using the Setlocal and Endlocal commands.) For a more detailed explanation of scopes, read the PowerShell help topic about_Scopes.
Scripts, Parameters, Functions and Scopes
If you’re not familiar with PowerShell scripts, parameters, functions and scopes, I hope this short discussion helps clarify how PowerShell is a dramatic improvement over Cmd.exe batch file scripting through consistent syntax, superior parameter management and automatic scope management. Once you get used to these PowerShell features, you’ll never want to go back to Cmd.exe batch files.
About the Author
You May Also Like