PowerShell For Anyone

by James Earnshaw on September 12, 2024

PowerShell is one of the tools I use everyday as a data professional. Like SQL Server Management Studio (SSMS) it's just there on my desktop, always open, ready when I need it.

My adoption was gradual, though. For a long time before I properly understood it, I kind of knew it would come in handy, but I just couldn't quite grok it. When I eventually sat down and tried to learn a bit of PowerShell it was hard and progress was slow. But, with a good book (Windows PowerShell in Action), it finally started to make sense. Now I use it all the time.

The number of uses I've found for PowerShell has grown steadily over the years and I keep finding new ones. This happens naturally. I'll discover a new trick by searching around for solutions to a problem I'm facing until I find a solution that roughly solves it (usually on Stack Overflow). Then I'll add the solution to my list of hacks.

I say hacks because I don't use PowerShell like a sysadmin. I basically just pick stuff up here and there. The scripts I write are (mostly) for me and are basically what I'd describe as hacks. There's no error handling and I use aliases (e.g., cd instead of Set-Location) But it doesn't matter because it works for me.

Quick Intro to PowerShell

It's not within the scope of this post to explain what PowerShell is in any detail. I'm assuming the reader has a vague idea of the following concepts: command lines, terminals, and shells. Basically you can control a computer by typing commands using a command line interface (CLI) that is displayed in a terminal program. The commands are interpreted by a shell program (e.g. PowerShell, Bash) that talks to the operating system and performs the work specified by the commands (e.g. system calls, etc.). Read a good explanation here.

PowerShell is a shell program for automating tasks on a Windows machine (it's actually cross platform but I've only ever used Windows). It comes installed on Windows and can be opened by searching for it, or with the shortcut: Windows Key + X, I.

The commands understood by PowerShell are called cmdlets (pronounced command-lets). The names of cmdlets are Verb-Noun pairs. For example, Set-Location, the cmdlet to change the current directory. Cmdlets can take parameters, e.g., -Path, a very common parameter, is the path to a file.

How I use PowerShell

I use PowerShell from within a terminal program called Windows Terminal. I like Windows Terminal because of the multiple tabs and panes feature. If you're using Windows 11 then it's now the default. I make sure Windows Terminal is ready on start-up by enabling it in the settings.

Now I'll list the PowerShell patterns and tips I use regularly. I start out with the basic stuff and progress to more advanced usage.

Listing files in a directory

A PowerShell session will be at a location, known as the current directory. For example:

PS C:\Windows>

PS means the terminal program is running a PowerShell session. This session is currently at the location C:\Windows. The > symbol is the prompt and indicates that the session is ready to receive commands.

The GUI equivalent would be having File Explorer open at C:\Windows. But, unlike File Explorer, you can't visually inspect the folder's contents. In a shell program, like PowerShell, that uses a command line interface (CLI), you have to enter the command that lists the folder's contents. In PowerShell the cmdlet (i.e. command) is Get-ChildItem:

current directory

After hitting enter the cmdlet is executed, the operating system handles the call, and output is returned to the terminal on the lines below the cmdlet. As you can see it returns all the files and directories in C:\Windows.

Aliases

I rarely write out the full command Get-ChildItem. I use aliases, shortform ways of writing commands. The alias for Get-ChildItem is gci, or, the one I use, dir, which is used in lots of other operating systems.

You can see all the aliases with: Get-Alias. Or the alias for Get-Alias: gal

get-alias

Note: I use the full version of the command when the script is part of a process and will be seen by other people, for maintainability's sake. It's helpful to see the full command for those unfamiliar with the alias.

If you see an alias in a script and don't know what it does you can find out with gal followed by the alias in question. E.g. you see spps and wonder what cmdlet that is. You can do gal spps:

spps

As can be seen spps is the alias for Stop-Process.

Locating files

The Get-ChildItem (or gci or dir) command from earlier has a -Path parameter which narrows the search. It's a good way to find a file or files within a directory, or sub-directory if you use the -Recursive parameter. It's flexible because you can use wildcards (*).

This example finds all the JSON files in the current directory and any subdirectories:

gci *.json -Recurse

Notice I didn't explicitly use the Path parameter because Path is a positional parameter and, because it's the first parameter of Get-ChildItem, it can be set by placing it right after Get-ChildItem.

Changing the working directory

Set-Location changes the current working directory. It's analogous to clicking around in File Explorer until you get to the folder containing the file you need. You will find yourself doing this a lot. The alias I use is cd, for current directory. Remember that paths in Windows use a backslash:

PS C:\Windows> cd C:
PS C:\>

So now the current directory is at C: (the root).

Clearing the terminal

The terminal window can quickly get crowded with cmdlets and their output. Clear it with Clear-Host. I actually had to look this up because I never write Clear-Host, I always use the alias cls. Another option for clearing the terminal is Ctrl + l. It's not a shortcut for Clear-Host, what it does is move the window down. If you scroll up to can see previous commands.

Viewing history

cls clears the screen but the history is still in memory and can be displayed with Get-History or simply h:

get-history

Notice how previously run cmdlets are numbered. Lets say you wanted to rerun a command. You can with # followed by the number, and then tab complete (i.e. hit the tab key).

Creating Directories

mkdir creates a new directory (or folder, I find myself using those words interchangeably):

# Creates directory "foo"
mkdir foo

You can also use New-Item:

New-Item -Type Directory foobar

But mkdir is one of those commands found in a lot of operating systems. It's one you "just know".

Files

Create files with New-Item or its alias ni. It takes a Path parameter, the name of the new file.

# Creates bar.txt
New-Item -Path bar.txt

# Creates somefile.txt, -Path omitted 
ni somefile.txt

Pipelines

It's a common practice in shell languages to "pipe" the output of one command to another command. This is done with the pipeline ("|") operator.

A common use case is sending the output of one cmdlet to a file. This is achieved with the Out-File cmdlet.

'This is some content' | Out-File out.txt

In the above example the text string "This is some content" is sent to a file. Another example is piping the output of Get-Date to a file.

Get-Date | Out-File date.txt

# a more concise way of doing it
Get-Date > date.txt

Reading files

Display a file's contents with Get-Content or its alias gc.

Get-Content .\date.txt

# output
# 10 September 2024 12:38:54

A useful pattern is copying and pasting a file's contents without opening it. Imagine you have a SQL script saved in a file (e.g., select.sql):

SELECT
*
FROM Sales.SalesOrderDetail

You can get its contents and pipe it to the clipboard (i.e. copy it) with:

Get-Content .\select.sql | clip

The script can now be pasted.

Copying and moving files

All the file system tasks you've always done in File Explorer (i.e., a GUI), like copying files from one folder to another, can be done in PowerShell, but they can be done much more powerfully. Not always, sometimes it's just quicker to use the File Explorer GUI, but I mostly use PowerShell for file system tasks. Working in File Explorer usually involves positioning two windows side by side, and dragging files across with the mouse, or something equally tedious.

Lets say you've got two directories foo and bar. foo contains the file date.txt and you need to either copy it or move it to the bar directory. Use Copy-Item (cp) or Move-Item (mv) respectively:

# Assuming foo and bar are located at the root of the C drive
# Copying
Copy-Item -Path C:\foo\date.txt -Destination C:\bar

# Moving
Move-Item -Path C:\foo\date.txt -Destination C:\bar

Testing for existence

When scripting it's useful to check a file or directory exists. Do that with Test-Path:

PS C:\> Test-Path .\foo\
True

Often Test-Path is used with the conditional command if. For example:

if (Test-Path C:\config.json) {
    # Parse the config
}

Joining paths

This one sometimes comes in handy:

PS C:\> Join-Path C: work
C:\work

Variables

You can't do anything non-trivial without being able to store and manipulate values in variables. Declare and assign variables with the syntax:

# an integer
$myInteger = 123

# a string
$myString = 'hello world'

Prefixing a string with $ declares a variable.

You can store the output of cmdlets in variables:

$content = Get-Content -Path date.txt

Finding text within files

I use this pattern a lot. And it's a game changer. Seriously.

Say you've got a directory full of text files and you need to know which file(s) contain a pattern. The cmdlet for doing this is Select-String.

Use this to set up the demo:

cd C:
mkdir somefiles
cd somefiles
1..10 | ForEach-Object {
    $file = $_.ToString() + ".txt"
    'Hello, World!' | Out-File $file
}

This snippet creates a directory called C:\somefiles and then creates 10 files (1.txt, 2.txt,..., 10.txt) inside it each containing the text "Hello, World!".

The first example of Select-String finds the pattern "hello" inside one file.

PS C:\work\somefiles> Select-String -Path .\1.txt -Pattern 'hello'

# output
# 1.txt:1:Hello, World!

This output indicates that a match was found. If you want to search every file in the directory then you would do this (sls being the alias of Select-String):

PS C:\somefiles> dir  | sls -Pattern 'hello'

1.txt:1:Hello, World!
10.txt:1:Hello, World!
2.txt:1:Hello, World!
3.txt:1:Hello, World!
4.txt:1:Hello, World!
5.txt:1:Hello, World!
6.txt:1:Hello, World!
7.txt:1:Hello, World!
8.txt:1:Hello, World!
9.txt:1:Hello, World!

Summary

That's a quick run through of some of the ways I use PowerShell regularly, the ones I can remember anyway. But I'm always finding new patterns and tricks.

PowerShell is really useful skill to learn. It's basically a Swiss Army Knife for Windows.