Skip to main content

PowerShell AToolBox: New-TestFile

After I ran across a way to create dummy files, I expanded on it. This one was an early challenge and one I was pretty happy with. The most difficult part was figuring out how to both allow the user to set a root file name and a custom extension while still infixing the number of the iteration between the two (to differentiate the filenames and keep from just overwriting what’s been created).

New-TestFile can create a specified number of random-sized files or a specified number of specified-sized files. It numbers the files and can take a custom filename root as one of its parameters. And as I often did at this stage of my development, I made a way to send the whole thing as a one-liner to your clipboard (for using on a remote machine that doesn’t have this function’s module installed).

CLI

Created files

Here’s the whole function.

<#
.Synopsis
Creates files that can be used for testing purposes.
.Description
Can create a specified number of random-sized files or a specified number of specified-sized files. The root name will always iterate--even for single test files--as in test1.dat, test2.dat, and so forth, but you can choose the root name and either include an extension or not.

Allows for specifying sizes from bytes to gigabytes, and there's no hard-cap on how many files it can create. The only hard cap is the function stopping when your target location has less than 10% free space available, although this has not been fully tested yet.

Also can provide a one-liner for use on other machines.
.Parameter Copy
Rather than creating files, this will send a one-liner to your clipboard for use on someone else's machine. Ignores the -number parameter but will adjust itself according to what you choose for the others.
.Parameter Number
Use to set a specified number of test files. Note that filenames automatically iterate: test1.dat, test2.dat, test3.dat, etc. Defaults to 1 and only accepts [long] numbers.
.Parameter Path
Set the target location. Defaults to c:\disks. Use -rootname for choosing the actual filenames.
.Parameter Randomize
Choose to randomize the size of the test file(s). Overrides -size and -unit parameters but creates no larger than in megabytes.
.Parameter RootName
Choose the root filename, as in the "test" and ".dat" portions of "test1.dat." Iteration numbers are automatic, and the function can handle root filenames with no extension or with multiple .s, as in "test.test.dat." Defaults to test.dat. Use -path to specify the target location.
.Parameter Size
Set the size of the created file(s). Use with the -unit parameter to set the size to a particular unit, like GB. Defaults to 42. Only accepts [long] numbers.
.Parameter Unit
Set the unit portion of the size of the created file(s). Use with the -size parameter. Defaults to MB. Acceptable inputs include b, kb, mb, and gb.
.Example
The following might be useful for testing OneDrive's max filecount. I'd set the path to somewhere in OneDrive, though. Also, I've set the size to 1 byte to keep from filling up the drive.

New-TestFile -size 1 -unit b -path c:\disks\test -rootname onedrive.test -number 300001
.Example
The following would create 100 files of the default root name test.dat in a c:\test directory and of random sizes.

New-TestFile -randomize -number 100 -path c:\test
.Inputs
.Outputs
.Notes
I still need to test the safety mechanism that's supposed to kill this function if the target drive has less than 10% of free space available.
.Link
.Component
.Role
.Functionality
#>
Function New-TestFile {
param(
[parameter(helpmessage="Enter the size. Defaults to 42.")][long]$Size = 42,
[parameter(helpmessage="Enter the unit; ex: B, KB, MB, GB. Defaults to MB.")][string]$Unit = "mb",
[parameter(helpmessage="Enter the directory to which you'd like to deposit the test file(s). Defaults to c:\disks\. Filename(s) will be some variation of test.dat.")][string]$Path = "c:\disks",
[parameter(helpmessage="Copies the function to a one-liner for a 42MB c:\disks\test.dat for use on other machines.")][switch]$Copy,
[parameter(helpmessage="Creates a specified number of test files. By default, they will be of random B, KB, and MB sizes, but you can pair this with the -size and -unit parameters to make a specified number of files all of the same, specified size.")][long]$Number = 1,
[parameter(helpmessage="Specifies that you want a random size for each test file. Ignores -size and -unit parameters. All random sizes will be in B, KB, or MB.")][switch]$Randomize,
[parameter(helpmessage="Choose the root name and extension. Ex: test.dat, file.test.")][string]$RootName = "test.dat"
)

#Cleaning up the $Path entry.
##Making sure it doesn't include a filename. Using a Select-String with a regular expression to match a .ext pattern would be better.
If ($Path -contains ".") {
Throw "It appears that you are trying to specify a filename/extension with the -path parameter. Specify just the path, please. If your path has a period in it, please remove the period. If you want to specify the root filename as well, use the -rootname parameter."
}

##Standardizing the path to not end with a \
$PathArray = ($Path.trim()).tochararray()
If (($PathArray[$PathArray.count - 1]) -eq "\"){
$PathArrayFixed = $PathArray[0..($PathArray.count -2)]
} Else {
$PathArrayFixed = $PathArray
}
$PathStandardized = $PathArrayFixed -join ""

##Creating the directory if it doesn't already exist.
If (!(Test-Path -path $PathStandardized)) {
New-Item -type directory -path $PathStandardized
}

#Converting units to total bytes for the creation function later.
If ($Unit -eq "b") {
$Size = $Size
} ElseIf($Unit -eq "kb") {
$Size = $Size * 1024
} ElseIf($Unit -eq "mb") {
$Size = $Size * 1048576
} ElseIf($Unit -eq "gb") {
$Size = $Size * 1073741824
} Else {
Throw "Sorry, what you've entered doesn't compute. For -unit, make sure you enter kb, mb, gb, or nothing (for just Bytes)."
}

#Making sure the $Number variable is usable.
If ($Number -lt 1) {
Throw "Please enter a positive integer. You're asking for the creation of a number of files here..."
}

#Prepping the full filename.
##Looking for an extension.
$RootNameArray = $RootName.tochararray()
If ("." -in $RootNameArray){
##Going through the $RootName variable backwards. Sends characters to the extension variable array until it finds the first ., at which time it switches to send characters to the raw variable for the rest of the root name.
$FileNameSwitch = $False
$FileNameExtensionArray = @()
$FileNameRawArray = @()
For ($NameIndex = $RootNameArray.length; $NameIndex -ge 0; $NameIndex--) {
If ($FileNameSwitch -eq $False) {
$FileNameExtensionArray += $RootNameArray[$NameIndex]
If ($RootNameArray[$NameIndex] -eq ".") {
$FileNameSwitch = $True
}
} Else {
$FileNameRawArray += $RootNameArray[$NameIndex]
}
}
##Reversing both arrays using the [array] class static method and then sending them to their string variable variants.
[array]::Reverse($FileNameExtensionArray)
$FileNameExtension = $FileNameExtensionArray -join ""
[array]::Reverse($FileNameRawArray)
$FileNameRaw = $FileNameRawArray -join ""
} Else {
##For file names without extensions.
$FileNameRaw = $RootName
$FileNameExtension = $Null
}

If ($Copy) {
If ($Randomize -eq $True) {
#Creates random file size with the max size set to 100 MB
$Size = Get-Random -maximum (100 * 1048576)
Set-Clipboard -value ('$File = [System.IO.File]::Create("' + $PathStandardized + '\' + $FileNameRaw + $FileNameExtension + '"); $File.SetLength(' + $Size + '); $File.Close()')
} Else {
Set-Clipboard -value ('$File = [System.IO.File]::Create("' + $PathStandardized + '\' + $FileNameRaw + $FileNameExtension + '"); $File.SetLength(' + $Size + '); $File.Close()')
}
} Else {
#For checking the freespace on the drive, to make sure this function doesn't fill up the drive.
[string]$TargetDrive = (Resolve-Path $PathStandardized).drive
##Gets the free space on that drive.
[long]$RealSize = Get-CIMInstance CIM_logicaldisk | Where-Object {$_.deviceid -eq ($TargetDrive + ":")} | Select-Object -expandproperty size
##The function will not run with less than 10% of disk space available.
$FreeSpaceThreshold = $RealSize * .1

#For iterating new files.
##Initializing iteration variables:
$PathContents = Get-ChildItem -path $PathStandardized | Select-Object *
##For the above, need to add the new items in the iterations to the list of items in $PathContents, so I don't have to get-childitem every iteration.
$PathFileNames = $PathContents | Select-Object -expandproperty name
##Might be useful Set-Variable ... -scope 1 for the parent of the current scope
$FileNumber = 0
$Iteration = 0
While ($Iteration -lt $Number) {
$Iteration++
#Checks to make sure >10% of disk space is left. I'm doing this rather than just doing math and keeping a tally of what amount the iterations are using because it seems safer, though it slows down the function some.
[long]$RealFreeSpace = Get-CIMInstance CIM_logicaldisk | Where-Object {$_.deviceid -eq $TargetDrive + ":"} | Select-Object -expandproperty freespace
If ($RealFreeSpace -le $FreeSpaceThreshold) {
Throw "The target disk has less than 10% of disk space left. This function will not run unless you have greater than 10% of disk space available."
}

#Cycles through test1.dat, test2.dat, and so on to find a free filename. When it does, it uses that filename for the creation function below.
$FileNumber++
$FileName = $FileNameRaw + "$FileNumber" + $FileNameExtension
While ($FileName -in $Pathfilenames) {
$Filenumber++
$FileName = "$FileNameRaw" + "$FileNumber" + "$FileNameExtension"
}

#Adds the good filename to the array of current filenames. I'm doing this to avoid having to get-childitem every iteration.
$PathFileNames += $FileName

#Joins the path and the good filename.
$FullFileName = $PathStandardized + "\" + $FileName

#Creates the file. If the $Random switch has been chosen, creates with a random size.
If ($Randomize -eq $True) {
#Creates random file size with the max size set to 100 MB
$Size = Get-Random -maximum (100 * 1048576)

#Final size check to make sure the file to be created doesn't go past 10% free space threshold
[long]$FreeSpaceFinal = $RealFreeSpace + $Size
If ($FreeSpaceFinal -le $FreeSpaceThreshold) {
Throw "Sorry, I don't think I can make a file and keep you over 10% free space available. This function will not run unless you have greater than 10% of space in the target disk available."
}

$File = [System.IO.File]::Create($FullFileName)
$File.SetLength($Size)
$File.Close()
} Else {
#Final size check to make sure the file to be created doesn't go past 10% free space threshold
[long]$FreeSpaceFinal = $RealFreeSpace + $Size
If ($FreeSpaceFinal -le $FreeSpaceThreshold) {
Throw "Sorry, I don't think I can make a file and keep you over 10% free space available. This function will not run unless you have greater than 10% of space in the target disk available."
}
$File = [System.IO.File]::Create($FullFileName)
$File.SetLength($Size)
$File.Close()
}
}
Write-Host "Created $Iteration file(s) with the root name" ('"' + "$FileNameRaw" + "$FileNameExtension" + '"') "in $PathStandardized."
}
#Need to make sure randomize is allowing for smaller sizes as well. They are all in the tens-of-thousands of kb.
}