PowerShell already provides excellent options in interactive mode. With your own scripts, you can even go a step further and permanently save commands that have been tested interactively.
Not only does this feature save future typing effort, but also avoids the need to memorize various commands, options, and so on.
To write scripts, you’ll need an editor. For your first experiments, Notepad is sufficient, but the free Notepad++ program provides significantly more functions. In the longer term, however, I recommend that you install Visual Studio Code (VS Code), if this program is not already available on your computer anyway. Whether you develop PowerShell, Bash, or Python scripts, VS Code provides excellent support. (What’s not recommended, however, is the PowerShell ISE development environment described in many older manuals. The program is no longer developed by Microsoft and is only for PowerShell versions up to 5.1.)
Hello, World!
The first script is supposed to output the text “Hello, World!” (Don’t worry, more serious examples will follow.) Open the editor of your choice, type the following line, and save the text file as Hello.ps1 in an easy-to-find directory (e.g., in Documents). The Write-Output cmdlet outputs the passed string as a parameter to the screen.
Write-Output "Hello, World!"
The *.ps1 file extension stands for PowerShell 1. When Microsoft released the second version of PowerShell, they did not want to change the then already established extension, and thus, it has absurdly remained *.ps1 to this very day.
To run the script, you simply need to enter the filename including the prepended directory, for example:
> Documents\Hello.ps1
Hello, World!
If the script is located in the current directory, you must prepend .\ to explicitly name the directory, for example:
> .\Hello.ps1
Hello, World!
In this context, “.” is a short notation for the directory you’re currently in. For security reasons, scripts without a directory specification are only executed if the *.ps1 files are located in a directory named in the $PATH environment variable.
Trouble with the Execution Policy
Depending on the Windows version, your first attempt to run a custom script will fail with the following error message:
> .\Hello.ps1:
File C:\Users\kofler\Documents\Hello.ps1 cannot be
loaded because running scripts is disabled on this system.
For more information, see about_Execution_Policies at
https://go.microsoft.com/fwlink/?LinkID=135170
The cause of this error message is what’s called the execution policy. This policy specifies the circumstances under which scripts are allowed to run on Windows. There are four possible settings:
- Restricted: No scripts can be executed at all.
- AllSigned: Only signed scripts can be executed.
- RemoteSigned: All custom scripts can be executed, but installed or downloaded scripts can be executed only if they have been signed.
- Unrestricted: All scripts can be executed.
Usually RemoteSigned applies on Windows Server, but Restricted applies for desktop versions:
> Get-ExecutionPolicy
Restricted
For this reason, on many Windows installations, scripts cannot be run at all. For security reasons, this default setting makes sense, but it is of course unsuitable for learning scripting. The following command allows the execution of custom scripts as well as signed foreign scripts for the current user:
> Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
You can also run the above command without the -Scope CurrentUser option—then, the setting will apply to all users of the computer. However, changing the execution policy at the system level is only allowed if you run PowerShell or Windows Terminal with admin privileges.
Even More Setting Options: The execution policy can be configured at different levels (machine, user, process). Details about the configuration options can be found at https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies.
4
What Does Signed Mean? A signed script contains a comment block with a digital signature (i.e., a cryptographic code). This code indicates who wrote the script. The code applies only to the state of the script at the time of the signature. Any subsequent change will invalidate the signature. You can sign your own scripts using Set-AuthenticodeSignature. This command requires that you have a certificate—either (for testing purposes) a self-created one or a real certificate from a certification authority. You can find more information on this topic at https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing.
Setting Up a Custom Script Directory
For your first script, where you save it doesn’t matter. But before you write more and more scripts over the next few weeks, a good idea is to set up a directory for your scripts and add that directory to the PATH environment variable. In our examples, I assume that you name the directory myscripts and create it directly in your working directory. Set-Location makes the user directory the current directory if not already the case. NewItem creates the directory. (If you have used cmd.exe or Bash so far, cd myscripts or mkdir myscripts will also get you there with less typing and headaches. But we want to make an effort in this chapter to present you with the “right” PowerShell cmdlets.)
> Set-Location
> New-Item -ItemType Directory myscripts
Now, we still need to set up the Path variable. This variable contains a list of all directories where PowerShell searches for executable programs and scripts. If you add the myscripts directory to this list, you can run scripts stored there without specifying the path. No matter which directory is currently active, it is now sufficient to enter Hello to start the Hello World script. Thanks to the Path variable, you don’t need to specify the location or the .ps1 identifier.
To configure this variable, search for Edit System Environment Variables in the Start menu and then click the Environment Variables button in the System Properties dialog. In the dialog box of the same name, select Path from your user variables and add your script directory via Edit > New > Browse.
Note that changes to the Path variable take effect only after restarting the terminal or PowerShell.
Running Scripts on Linux and macOS
I generally assume in this chapter that you’re using Windows. But of course, you can also run PowerShell scripts on Linux or macOS. Although no execution policy exists in Linux or macOS, other rules must be observed, such as the following.
First, the script file must start with the what’s called the shebang line:
#!/usr/bin/env pwsh
This line specifies that the env command must search for the pwsh shell and execute the following code with it. For platform-independent scripts, you can also use this line on Windows, in which case this line is simply considered a comment with no effect.
On the other hand, you must mark the script file, via chmod as executable (hence, x):
$ chmod +x Hello.ps1 (Linux)
$ chmod a+x Hello.ps1 (macOS)
Once you have met these two requirements, you can run the script on Linux and macOS:
$ ./Hello.ps1
Hello, World!
Example: Cleaning Up the Downloads Directory
After our minimalistic Hello World script, let’s consider a more complex example. In this second example: The Tidy-Downloads.ps1 script is supposed to search the n largest files from the downloads directory and delete them after a query. In this way, you can quickly create space in a minimal amount of time. (After all, usually just a few huge files take up most of the space.)
Note these two major features of our program:
- The script uses the DownPath variable, which contains the location of the downloads directory. The easiest way to determine this location would be $DownPath = "$HOME\Downloads". However, this procedure does not work in every case (see https://stackoverflow.com/questions/57947150). For example, some users might have set different locations for their download directories. The approach taken in this case is a bit more cumbersome and uses the Known Folders API. However, this method also has a disadvantage: It works only on Windows, not on Linux or macOS.
- The number of files to be deleted can be passed to the script via an optional parameter. If the parameter is missing, the script searches for the ten largest files.
In PowerShell, variables are prefixed with a dollar sign ($) character. All parameters of a script or function are declared using param(), where you can specify a data type, a default value, and a lot of other additional information. (Unless you use functions, param must be the first statement in the script!)
# Sample file Tidy-Downloads.ps1
# $NoOfFiles specifies how many files to delete
# (by default: 10)
param([int] $NoOfFiles = 10)
# $DownPath contains the location of the Downloads directory
$DownPath = (New-Object -ComObject Shell.Application).
NameSpace('shell:Downloads').Self.Path
# delete the largest files in the downloads directory with
# query
Get-ChildItem -Path $DownPath |
Sort-Object -Property Length -Descending |
Select-Object -First $NoOfFiles |
Remove-Item -Confirm
Comments: As with most scripting languages, comments in PowerShell scripts are introduced with the # character and then extend to the end of the line. Multiline comments are introduced with <# and end with #>. Such comments are especially intended to formulate help texts for Get-Help. The syntax is for comments is documented at https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help.
Differentiating Between PowerShell Versions
On many computers, PowerShell versions 5.n and 7.n are installed in parallel. Nevertheless, all scripts share the *.ps1 file extension. If your code explicitly requires a specific PowerShell version or has other requirements, you must include one or more # Requires statements in the script:
- #Requires -Version <n>: The script can be executed only with the specified or a newer version.
- #Requires -PSEdition Core: The script can be executed only with the core variant (applies to all PowerShell versions from 6.0).
- #Requires -PSEdition Desktop: The script can be executed only with the desktop variant (all Windows PowerShell versions up to and including 5.1).
- #Requires -Modules <name>: The script requires the specified module.
- #Requires -RunAsAdministator: The script can only be executed with admin rights. #Requires statements can be placed anywhere in the script.
Editor’s note: This post has been adapted from a section of the book Scripting: Automation with Bash, PowerShell, and Python by Michael Kofler.
Comments