April 21st, 2021

Borrowing a built-in PowerShell command to create a temporary folder

Sean Kearney
Customer Engineer - Microsoft

Q: Hey I have a question for you. It seems silly and I know I could probably put something together with Get-Random. But can you think of another way to create a temporary folder with a random name in PowerShell?

Ideally, I’d like it to be in a user’s own “Temporary Folder” is possible.

A: We sure can! If Doctor Scripto was sitting here right now, I’d see that little green haired person shout out “Never fear, Scripto is here!”

New-TemporaryFile Cmdlet

Within PowerShell there is a built in Cmdlet called New-TemporaryFile. Running this cmdlet simply creates a random 0 byte file in the $ENV:Temp folder in whichever platform you are working in.

However, we can borrow the filename created and use it to create a folder instead. It’s not really difficult, but maybe just not thought of very often.

When we execute the following cmdlet we get output similar to this as it generates a new 0 Byte random file in the User’s Temp folder stored in $ENV:Temp

PS> New-TemporaryFile

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         3/31/2021   9:25 PM              0 tmpA927.tmp

Ok, that really wasn’t that impressive but what if we were to do this instead?

$File = New-TemporaryFile

Now we’ve created the file and stored it away in the $File object. With this we can remove the file of course using the Remove-Item cmdlet

Remove-Item -path $File -force

HA! I’ve already saved some time! The $File object is still there with the information I want to use.

So, I could access the name in the object property and use it to create a directory instead in the following manner.

New-Item -ItemType Directory -Path $File.Name

But the problem is that it would be in whatever default folder PowerShell was looking into at the time.

Hmmmmm…. How to solve that?

But there is a built in variable called $ENV:Temp which targets the exact Temporary folder that the New-TemporaryFile cmdlet uses as well!

I can then take that variable and the original name of the Temporary file and combine them together like this.

$ENV:Temp + '\' + $File.Name

or

I can even put them together in a single String like this.

"$($ENV:Temp)\$($File.Name)"

With this I could just create a new temporary directory under our temp folder in this manner.

New-Item -ItemType Directory -Path "$($ENV:Temp)\$($File.Name)"

Now to identify where the file ended up, I could same thing as last time by storing it as an object like $DirectoryName if I wanted. Then I could remove the “Random Directory name” later if I needed to.

$Folder=New-Item -ItemType Directory -Path "$($ENV:Temp)\$($File.Name)"

Then when I am done with that folder that was presumably used to hold some garbage data. I can just use Remove-Item again.

But because it’s a directory, I need to add -recurse -force to ensure all data and Subfolders are removed.

Remove-Item -Path $Folder -Recurse -Force

But here is the fun and neat bit. If you needed on a regular basis, we could make this into a quick function for your code, module or to impress friends with!

Function New-TemporaryFolder {
    # Create Temporary File and store object in $T
    $File = New-TemporaryFile

    # Remove the temporary file .... Muah ha ha ha haaaaa!
    Remove-Item $File -Force

    # Make a new folder based upon the old name
    New-Item -Itemtype Directory -Path "$($ENV:Temp)\$($File.Name)" 
}

Now at this point I had thought my journey was complete. It was until I posted the solution to the Facebook group for the PowerShell Community Blog to share.

A fellow member of the Community noted the approach, while neat, was not very efficient.

At that point I dug into the code on Github for the open source version of PowerShell 7.x to see how it was done there.

In reading the source code for New-TemporaryItem I was able to see the .NET object being used to generate the file. It turns out there is also a .NET method that can be used to create just that temporary name which all I wanted to use in the first place for the directory name.

When I ran this in the PowerShell Console it produced the following output of a New Temporary Folder

PS> [System.IO.Path]::GetTempFileName()
C:\Users\Administrator\AppData\Local\Temp\2\tmp3864.tmp

This was exactly what I wanted, that random temporary Name to be consumed for the New-Item Cmdlet. With this approach the function became a lot simpler and far more efficient!

Function New-TemporaryFolder {
    # Make a new folder based upon a TempFileName
    New-Item -ItemType Directory -Path([System.IO.Path]::GetTempFileName())
}

But alas my victory was short lived. This method still created the file, it didn’t just display the name. So the function ended up failing.

But since really I just wanted that format to be used for the temporary directory. Plus the format for the temporary filename was as simple as tmpxxxx.tmp where the xxxx was a random hexadecimal number, I came up with a better idea!

Just create a number between 0 and 65535 with Get-Random and use the [convert] accelerator to change it the 4 character Hexadecimal number instead.

The end result looked like this and gave me the desired result I wanted.

PS> "$($Env:temp)\tmp$([convert]::tostring((get-random 65535),16).padleft(4,'0')).tmp"
C:\Users\doctorscripto\AppData\Local\Temp\tmp5633.tmp

Now I ended up with a working function that could produce the desired output I wanted and in a more efficient manner.

Function New-TemporaryFolder {
    # Make a new folder based upon a TempFileName
    $T="$($Env:temp)\tmp$([convert]::tostring((get-random 65535),16).padleft(4,'0')).tmp"
    New-Item -ItemType Directory -Path $T
}

Why did all of this pop into my head? I was actually creating some PowerShell for customer and needed a consistent and random set of folders in a common and easily erasable location.

I was hoping that we had a New-TemporaryDirectory cmdlet, but found it was just as easy to write one by borrowing an existing cmdlet.

It was fun as well to discover how I could improve on the solution by reading the Source code for New-TemporaryItem on Github.

Thanks to a little nudging from the Community. So a big Thank you to Joel Bennett for the critique! 🙂

Sean Kearney – Customer Engineer/Microsoft – @PowerShellMan

“Remember with great PowerShell comes great responsibilty…”

Author

Sean Kearney
Customer Engineer - Microsoft

6 comments

Discussion is closed. Login to edit/delete existing comments.

  • Grote, Justin · Edited

    Did you test your last example? Because your problem still is that a file gets created by using that GetTempFileName function, and it will error with "An item already exists"

    Instead you can pretty easily use New-Guid to get a temporary file path without creating a file and having to remove it. This assumes your app can use long folder names which is pretty much anything made in the last 25 years

    In Powershell 7 there is...

    Read more
    • Sean KearneyMicrosoft employee Author

      Good catch! I remember trying it out and had thought I had “just gotten the filename” but you’re correct. My “New Improved solution” certainly didn’t cut it as that method not only generates the filename but also the file itself. DOH!

      But as you can see from the comments for a temporary folder, there are multiple solutions. I was just trying to “reuse” the one from the New-TemporaryFile for fun.

      Read more
      • Gustavo Rossi

        I liked the idea of “reusing” the New-TemporaryFile code, so I came up with the following solution:

        New-Item -ItemType Directory -Path ([System.IO.Path]::GetTempPath()) -Name ([System.IO.Path]::GetRandomFileName()) -WhatIf
    • Fred Jacobowitz

      Thanks Justin

    • Mystery Man · Edited

      👍

      I got the same results with Windows PowerShell and Powershell 7.1.

      I think New-Guid is overkill. Get-Random is a better alternative. Also, you don't need to mix Join-Path with New-Item because the New-Item cmdlet already has -Path and -Name properties. Try this:

      <code>

      Read more