Running Windows PowerShell Scripts
Few things in life are as exciting as getting a brand-new command shell and scripting language; in fact, getting a brand-new command shell and scripting language is soexciting that you can barely get the thing out of the box before you want to take it for a spin. Those of you who’ve downloaded Windows PowerShell know exactly what we’re talking about: if you’re like most people, the very moment the installation process finished you double-clicked a .PS1 file (.PS1 being the file extension for Windows PowerShell scripts), sat back, and waited for the magic to happen.
As it turned out, however, this is what happened:
Hmmm, instead of running, your script opened up in Notepad. Interesting, but not exactly what you had in mind. Oh wait, you think, I get it: you probably have to run Windows PowerShell before you can run a Windows PowerShell script. OK, that makes sense. And so, with that in mind, you open up Windows PowerShell and type the path to the .PS1 file at the command prompt. You press ENTER and wait for the magic to happen:
As it turned out, however, this is what happens:
File C:\scripts\test.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get- help about_signing" for more details. At line:1 char:19 + c:\scripts\test.ps1 <<<<
Wow; how nice. A new command shell and scripting environment that doesn’t even let you run scripts. What will those guys at Microsoft think of next?
Listen, don’t panic; believe it or not, everything is fine. You just need to learn a few little tricks for running Windows PowerShell scripts. And the Scripting Guys are here to help you learn those tricks.
Running Scripts From Within Windows PowerShell
Let’s start with running scripts from within Windows PowerShell itself. (Which, truth be told, is probably the most common way to run Windows PowerShell scripts.) Why do you get weird error messages when you try to run a script? That’s easy. The security settings built into Windows PowerShell include something called the “execution policy;” the execution policy determines how (or if) PowerShell runs scripts. By default, PowerShell’s execution policy is set to Restricted; that means that scripts – including those you write yourself – won’t run. Period.
|Note. You can verify the settings for your execution policy by typing the following at the PowerShell command prompt and then pressing ENTER:
Now, admittedly, this might seem a bit severe. After all, what’s the point of having a scripting environment if you can’t even run scripts with it? But that’s OK. If you don’t like the default execution policy (and you probably won’t) then just go ahead and change it. For example, suppose you want to configure PowerShell to run – without question – any scripts that you write yourself, but to run scripts downloaded from the Internet only if those scripts have been signed by a trusted publisher. In that case, use this command to set your execution policy to RemoteSigned:
Alternatively, you can set the execution policy to AllSigned (all scripts, including those you write yourself, must be signed by a trusted publisher) or Unrestricted (all scripts will run, regardless of where they come from and whether or not they’ve been signed).
See? No need to need to panic at all, is there?
|Note. Not sure what we mean by “signing scripts?” Then open up PowerShell, type the following, and press ENTER:
Or, even better, download our Windows PowerShell Graphical Help File and read the same topic in standard Windows help format.
After you change your execution policy settings it’s possible to run scripts. However, you still might run into problems. For example, suppose you change directories from your Windows PowerShell home directory to C:\Scripts (something you can do by typing cd C:\Scripts). As it turns out, the C:\Scripts folder contains a script named Test.ps1. With that in mind you type the following and the press ENTER:
And here’s the response you get:
The term 'test.ps1' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again. At line:1 char:7 + test.ps1 <<<<
We know what you’re thinking: didn’t we just change the execution policy? Yes, we did. However, this has nothing to do with the execution policy. Instead, it has to do with the way that PowerShell handles file paths. In general, you need to type the complete file path in order to run a script. That’s true regardless of your location within the file system. It doesn’t matter if you’re in C:\Scripts; you still need to type the following:
Now, we said “in general” because there are a couple of exceptions to this rule. For example, if the script happens to live in the current directory you can start it up using the .\notation, like so:
|Note. There’s no space between the .\ and the script name.|
And while PowerShell won’t search the current directory for scripts it will search all of the folders found in your Windows PATH environment variable. What does that mean? That means that if the folder C:\Scripts is in your path then you can run the script using this command:
But be careful here. Suppose C:\Scripts is not in your Windows path. However, suppose the folder D:\Archive is in the path, and that folder also contains a script named Test.ps1. If you’re in the C:\Scripts directory and you simply type Test.ps1 and press ENTER, guess which script will run? You got it: PowerShell won’t run the script in C:\Scripts, but it will run the script found in D:\Archive. That’s because D:\Archive is in your path.
Just something to keep in mind.
|Note. Just for the heck of it, here’s a command that retrieves your Windows PATH environment variable and displays it in a readable fashion:
$a = $env:path; $a.Split(";")
Even More About File Paths
Now we know that all we have to do is type in the full path to the script file and we’ll never have to worry about getting our scripts to run, right? Right.
Well, almost right. There’s still the matter of scripts whose path name includes a blank space. For example, suppose you have a script stored in the folder C:\My Scripts. Try typing this command and see what happens:
Of course, by now you’ve come to expect the unexpected, haven’t you? Here’s what you get back:
The term 'C:\My' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again. At line:1 char:8 + C:\My <<<< Scripts\Test.ps1
This one you were able to figure out on your own, weren’t you? Yes, just like good old Cmd.exe, PowerShell has problems parsing file paths that include blank spaces. (In part because blank spaces are how you separate command-line arguments from the call to a script.) In Cmd.exe all you can work around this problem by enclosing the path in double quotes. Logically enough, you try the same thing in PowerShell:
And here’s what you get back:
Um, OK …. You try it again. And here’s what you get back:
You try it – well, look, there’s no point in trying it again: no matter how many times you try this command, PowerShell will simply display the exact same string value you typed in. If you actually want to execute that string value (that is, if you want to run the script whose path is enclosed in double quotes) you need to preface the path with the Call operator (the ampersand). You know, like this:
& "C:\My Scripts\Test.ps1"
|Note. With this particular command you can either leave a space between the ampersand and the path name or not leave a space between the ampersand and the path name; it doesn’t matter.|
To summarize, here’s how you run from scripts from within Windows PowerShell:
- Make sure you’ve changed your execution policy. By default, PowerShell won’t run scripts at all, no matter how you specify the path.
- To run a script, specify the entire file path, or either: 1) use the .\ notation to run a script in the current directory or 2) put the folder where the script resides in your Windows path.
- If your file path includes blank spaces, enclose the path in double quote marks and preface the path with an ampersand.
And, yes, that all takes some getting used to. However, you will get used to it. (To make life easier for you, we recommend that you keep all your scripts in one folder, such as C:\Scripts, and add that folder to your Windows path.)
|Note. So can you use PowerShell to add a folder to your Windows Path? Sure; here’s a command (that we won’t bother to explain in this introductory article) that tacks the folder C:\Scripts onto the end of your Windows path:
$env:path = $env:path + ";c:\scripts"
Bonus: “Dot Sourcing” a Script
Admittedly, up to this point the news hasn’t been all that good: you can’t run a PowerShell script by double-clicking the script icon; PowerShell doesn’t automatically look for scripts in the current working directory; spaces in path names can cause all sorts of problems; etc. etc. Because of that, let’s take a moment to talk about one very cool feature of Windows PowerShell scripting: dot sourcing.
Suppose we have a very simple VBScript script like this one:
A = 5 B = 10 C = A + B
If you run this script from the command window, the script will run just fine. However, because we forgot to include an Echo statement we won’t see anything happen onscreen. Because of that we’ll never know the value of C. Sure, we could try typing Wscript.Echo C at the command prompt, but all we’ll get back is the following error message:
'Wscript.echo' is not recognized as an internal or external command, operable program or batch file.
That should come as no surprise: scripts are scripts, the command window is the command window, and ne’er the twain shall meet. Sure, it would be nice if the command window had access to values that were assigned in a script (and vice-versa), but it ain’t gonna happen.
At least not in VBScript.
Now, let’s consider a Windows PowerShell counterpart to our VBScript script:
$A = 5 $B = 10 $C = $A + $B
Suppose we run this script, then type $C at the command prompt. What do you think we’ll get back? If you guessed nothing, then you guessed correctly:
In other words, we don’t get back anything at all. Which, again, should come as no great surprise. Come on, Scripting Guys; shouldn’t this be leading us somewhere?
Yes, it should. And believe it or not, it is. Let’s run our PowerShell script again, only this time let’s “dot source” it; that is, let’s type a period and a blank space and then type the path to the script file. For example:
When we run the script nothing will seem to happen; that’s because we didn’t include any code for displaying the value of $C. But now try typing $C at the command prompt . Here’s what you’ll get back:
Good heavens! Was this a lucky guess on the part of the PowerShell console, or is this some sort of magic?
Surprisingly enough, it’s neither. Instead, this is dot sourcing. When you dot source a script (that is, when you start the script by prefacing the path to the script file with a dot and a blank space) any variables used in the script become global variables that are available in multiple scopes. What does that mean? Well, a script happens to represent one scope; the console window happens to represent another scope. We started the script Test.ps1 by dot sourcing it; that means that the variable $C remains “alive” after the script ends. In turn, that means that this variable can be accessed via the command window. In addition, these variables can be accessed from other scripts. (Or at least from other scripts started from this same instance of Windows PowerShell.)
Suppose we have a second script (Test2.ps1) that does nothing more than display the value of the variable $C:
Look what happens when we run Test2.ps1 (even if we don’t use dot sourcing when starting the script):
Cool. Because $C is a global variable everyone has access to it.
And, trust us here: this is pretty cool. For example, suppose you have a database that you periodically like to muck around with. If you wanted to, you could write an elaborate script that includes each and every analysis you might ever want to run on that data. Alternatively, you could write a very simple little script that merely connects to the database and returns the data (stored in a variable). If you dot source that script on startup you can then sit at the command prompt and muck around with the data all you want. That’s because you have full access to the script variables and their values.
|Note. OK, sure, this could cause you a few problems as well, especially if you tend to use the same variable names in all your scripts. But that’s OK; if you ever need to wipe out the variable $C just run the following command (note that, with the Remove-Variable cmdlet, we need to leave off the $ when indicating the variable to be removed):
Play around with this a little bit and you’ll start to see how useful dot sourcing can be.
Running Scripts Without Starting Windows PowerShell
We realize that it’s been awhile, but way back at the start of this article we tried running a Windows PowerShell script by double-clicking a .PS1 file. That didn’t go quite the way we had hoped: instead of running the script all we managed to do was open the script file in Notepad. Interestingly enough, that’s the way it’s supposed to work: as a security measure you can’t start a PowerShell script by double-clicking a .PS1 file. So apparently that means that you do have to start PowerShell before you can run a PowerShell script.
In a somewhat roundabout way, that’s technically true. However, that doesn’t mean that you can’t start a PowerShell script from a shortcut or from the Run dialog box; likewise you can run a PowerShell script as a scheduled task. The secret? Instead of calling the script you need to call the PowerShell executable file, and then pass the script path as an argument to PowerShell.exe. For example, in the Run dialog box you might type a command like powershell.exe -noexit c:\scripts\test.ps1:
There are actually three parts to this command:
- Powershell.exe, the Windows PowerShell executable.
- -noexit, an optional parameter that tells the PowerShell console to remain open after the script finishes. Like we said, this is optional: if we leave it out the script will still run. However, the console window will close the moment the script finishes, meaning we won’t have the chance to view any data that gets displayed to the screen.Incidentally, the -noexit parameter must immediately follow the call to the PowerShell executable. Otherwise the parameter will be ignored and the window will close anyway.
- C:\Scripts\Test.ps1, the path to the script file.
What if the path to the script file contains blank spaces? In that case you need to do the ampersand trick we showed you earlier; in addition, you need to enclose the script path in single quote marks, like so:
powershell.exe -noexit &'c:\my scripts\test.ps1'
Strange, but true!
|Note. Here’s an interesting variation on this same theme: instead of starting PowerShell and asking it to run a particular script you can start PowerShell and ask it to run a particular command. For example, typing the following in the Run dialog box not only starts PowerShell but also causes it to run the Get-ChildItem cmdlet against the folder C:\Scripts:
powershell.exe -noexit get-childitem c:\scripts
It’s possible to get even more elaborate when starting Windows PowerShell, but this will do for now. If you’d like more information on PowerShell startup options just typepowershell.exe /? from either the Windows PowerShell or the Cmd.exe command prompt.
By the way, this is the same approach you need to use if you want to run a Windows PowerShell script as part of a logon script. You can’t simply assign a .PS1 file as a logon script; the operating system won’t know what to do with that. Instead, you’ll need to create a VBScript script that calls the PowerShell script:
Set objShell = CreateObject("Wscript.Shell") objShell.Run("powershell.exe -noexit c:\scripts\test.ps1")
Assign this VBScript script as the logon script and everything should work just fine. (Assuming, of course, that you’ve installed Windows PowerShell on any computers where this logon script is going to run.)