PowerShell: Using Subexpressions Within Strings

PowerShell

Much like a poorly crafted sandwich, have you ever tried to access properties of an object from within a string only to have it fall apart?

Good news, everybody! Subexpressions are the answer! (Not to your sandwich woes, though. You’ll still need to work on that one).

Did you know that you can do subexpressions within your strings? *le gasp!* Did you also know that you can use subexpressions to access object properties within strings? *le double gasp!*

You can use the subexpression operator $() to do some awesome things within strings. For more information on PowerShell operators, check out this link.

Subexpressions

First and foremost, what in the world is a subexpression? Or even an expression for that matter?!

Simply put, an expression is a statement that can be evaluated and gives a result.

A subexpression is an expression within an expression…think Inception.

Let’s do some math to demo how to use subexpressions. PowerShell can already do math pretty easily, so we’ll do some basic addition.

2+2

Awesome. Yay math.

If we try using that within a string, however, it ends up printing it instead of evaluating it.

Write-Output "2+2"

Now, let’s use the subexpression operator to show you how to perform all the maths within a string. This will evaluate the expression 2 + 2 within the double quotes.

Write-Output "$(2+2)"
Write-Output "2 + 2 = $(2+2)"

You can see that we can seamlessly combine our text and our subexpression to get the output that we’re looking for!

That’s PowerShell magic at work right there.

Now, what about the promise of using subexpressions to access object properties?! Coming right up!

Using subexpressions within strings to access object properties

We can extend the use of subexpressions to access object properties.

First, let’s start our example by showing what many people are tempted to do within strings.

Let’s say that we want to show the Id for our PowerShell process within our output.

$MyPowerShellProcess = Get-Process powershell
$MyPowershellProcess.Id

Easy peasy, right? Based off of this result, you might be tempted to use the following code to output the process id within a string:

$MyPowerShellProcess = Get-Process powershell
Write-Output "My PowerShell process ID is: $MyPowershellProcess.Id"

Sadly, you’ll notice this isn’t the output you expected. You’ll need to access the property from a subexpression.

Here’s how you can use subexpressions to get the object properties.

$MyPowershellProcess = Get-Process powershell
Write-Output "My PowerShell process ID is: $($MyPowershellProcess.Id)"

This is fantastic! We could even take this a step further and gather all this info from within the subexpression itself.

Write-Output "My PowerShell process ID is: $(Get-Process powershell | Select -ExpandProperty Id)"

Behold the power!

Wrapping up

Yes, it’s true. Gone are the days of simply assigning values to variables like animals. Embrace the power of the subexpression!

…Just kidding.

All I ask is that you continue making your code easier to read and use variables too. Pretty please.

Happy PowerShelling!

PS: There are other ways to access that information within a string, such as assigning the value directly to a variable or using the format operator. But, we’ll save that for another day.

To see this in action, please watch this awesome video:

3 Comments

  • Thought you might have this up your sleeve..

    I have 1 million plus files I’m trying to wrestle into submission and there names contain various prefix’s/random garbage but most have the desired info as some segment within the name, tough you might be able to do this in PS??

    So I want set a var of “X” # chars matching and then use that match to create a dir using that match as the name then move the file into it?

    This python script is as close as I’ve come up with but It’s using the 1st 7 chars vs. any matching string?

    import os
    import re
    import shutil

    for root, dirs, files in os.walk(dirname):
    for fname in files:
    # Match a string starting with 7 digits followed by everything else.
    # Capture each part in a group so we can access them later.
    match_object = re.match(‘([0-9]{7})(.*)$’, fname)
    if match_object is None:
    # The regular expression did not match, ignore the file.
    continue

    # Form the new directory path using the number from the regular expression and the current root.
    new_dir = os.path.join(root, match_object.group(1))
    if not os.path.isdir(new_dir):
    os.mkdir(new_dir)

    new_file_path = os.path.join(new_dir, match_object.group(2))

    # Or, if you don’t want to change the filename, use:
    new_file_path = os.path.join(new_dir, fname)

    old_file_path = os.path.join(root, fname)
    shutil.move(old_file_path, new_file_path)

    • Hello and thanks for the comment! Something like this is definitely possible in PowerShell.

      As a test setup, I created 1000 randomly generated filenames that included specific words/patterns in the middle.

      For example, some of the files I created looked like this:

      6yfjsebtmoqawAnotherFileNamexbfrznvcy820o.txt
      089zchnxuerpyAnotherFileName8bsqwf9ajgemn.exe
      mrgoqbt8c94e1MyFilesx75fkghm6tbvl.log
      rizl69mu8w4q1MyFilesgfwrmcdk25s69.blah
      js0jfjfljsla-123456-fjasfa-asfasjf.txt
      0jhpjfcnnqwo-123456-asfapsajncs.exe

      As you can see, it’s random garbage text and random file extensions, but they contain either “AnotherFileName”, “MyFiles”, or even a number sequence 123456 in the middle of the name.

      In my example script, I search in a given directory and look for files that match either pattern. If they match, I create a subdirectory matching the name of the pattern and move the file into that newly-created subdirectory.

      $StartingDirectory = “C:\PathToYourFiles”
      $Patterns = “ValidFileName”, “AnotherFileName”, “[0-9]{6}”

      Foreach ($pattern in $Patterns) {

      If (Get-ChildItem $StartingDirectory -File | Where-Object {$_.Name -match $Pattern}) {

      $NewDirectory = Join-Path $StartingDirectory $Matches[0]

      If (-not (Test-Path $NewDirectory )) {

      New-Item -Path $NewDirectory -ItemType Directory

      }

      Get-ChildItem $StartingDirectory -File | Where-Object {$_.Name -match $Pattern} | ForEach-Object {

      Move-Item -Path $_.FullName -Destination $NewDirectory

      }
      }
      }

      This is a quick example of what you could do, so feel free to modify this to your requirements. Hopefully it will give you a springboard for what you need. PowerShell handles Regular Expressions easily enough, so feel free to go hog wild with your patterns. To demonstrate that, I included a very simple example of matching 6 numbers in a row as part of the patterns in my script.

      Cheers and best of luck to you!

Leave a Reply

Your email address will not be published. Required fields are marked *