Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
March 18, 2025

Drag and Drop PowerShell

Today I'm going back to my old school DOS batch file days and bring back an automation technique to the PowerShell world. Let me show you how to bring drag and drop automation to your PowerShell scripts. This technique doesn't necessarily scale very well, but it is an interesting way to pass data to your code. After reading this and testing it out, you'll have to think about where it makes sense for you.

Dropping to a File

Back in the day you could create a simple batch file like this:

@echo off
REM DropIt.bat
echo You dropped %1
pause

The only thing this batch file does is echo the argument, %1.

C:\> c:\work\drop\dropit.bat "Testing 123"
You dropped "Testing 123"
Press any key to continue . . .

I've copied this batch file to C:\Work\Drop.

DropIt Batch File
DropIt Batch File

From here, I can drag and drop an item from the file explorer on to the batch file. I'll drag my Downloads folder and drop it on the DropIt.bat file.

Dropping an item
Dropping an item

You can see the prompt that I'm going to open the dropped item with DropIt.bat. As soon as I release my mouse, a DOS prompt will open and echo the dropped item.

DOS result
DOS result

This suggests that I could pass the path of a file or folder to a script by dragging and dropping it on to the script file. However, there is a catch for our PowerShell needs. For security reasons, you can't arbitrarily invoke a PowerShell script. Which means you can't simply replace the DOS batch file with a PowerShell script. Instead, you'll need to wrap the PowerShell script in a batch file.

I'm going to test with this function.

#requires -version 7.5
#Get-FileExtensionInfo.ps1
using namespace System.Collections.generic

Function Get-FileExtensionInfo {
    [cmdletbinding()]
    [alias("gfei")]
    [OutputType("FileExtensionInfo")]
    Param(
        [Parameter(
            Position = 0,
            ValueFromPipeline,
            HelpMessage = "Specify the root directory path to search"
        )]
        [ValidateNotNullOrEmpty()]
        [ValidateScript( { Test-Path $_ })]
        [string]$Path = ".",
        [Parameter(HelpMessage = "Recurse through all folders.")]
        [switch]$Recurse,
        [Parameter(HelpMessage = "Include files in hidden folders")]
        [switch]$Hidden,
        [Parameter(HelpMessage = "Add the corresponding collection of files")]
        [Switch]$IncludeFiles
    )

    Begin {
        $ver = "1.1.0"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Starting $($MyInvocation.MyCommand) v$ver"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Running PowerShell version $($PSVersionTable.PSVersion)"
        Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Using PowerShell Host $($Host.Name)"
        #capture the current date and time for the audit date
        $report = Get-Date

        $enumOpt = [System.IO.EnumerationOptions]::new()
        if ($Recurse) {
            Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Getting files recursively"
        }
        $enumOpt.RecurseSubdirectories = $Recurse
        if ($Hidden) {
            Write-Verbose "[$((Get-Date).TimeOfDay) BEGIN  ] Including hidden files"
            $enumOpt.AttributesToSkip -= 2
        }
        #initialize a list to hold the results
        $list = [list[object]]::new()

    } #begin

    Process {
        #convert the path to a file system path
        $cPath = Convert-Path -Path $Path

        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Processing $cPath"
        $dir = Get-Item -Path $cPath
        $files = $dir.GetFiles('*', $enumOpt)

        Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Found $($files.count) files"
        $group = $files | Group-Object -Property extension

        #Group and measure
        foreach ($item in $group) {
            Write-Verbose "[$((Get-Date).TimeOfDay) PROCESS] Measuring $($item.count) $($item.name) files"
            $measure = $item.Group | Measure-Object -Property length -Minimum -Maximum -Average -Sum

            #create a custom object
            $out = [PSCustomObject]@{
                PSTypeName   = "FileExtensionInfo"
                Path         = $cPath
                Extension    = $item.Name.Replace('.', '')
                Count        = $item.Count
                TotalSize    = $measure.Sum
                SmallestSize = $measure.Minimum
                LargestSize  = $measure.Maximum
                AverageSize  = $measure.Average
                Computername = [System.Environment]::MachineName
                ReportDate   = $report
                Files        = $IncludeFiles ? $item.group : $null
                IsLargest    = $False
            }
            $list.Add($out)
        }
    } #process

    End {
        #mark the extension with the largest total size
        ($list | Sort-Object -Property TotalSize,Count)[-1].IsLargest = $true
        #write the results to the pipeline
        $list
        Write-Verbose "[$((Get-Date).TimeOfDay) END    ] Ending $($MyInvocation.MyCommand)"
    } #end
}

Update-TypeData -TypeName FileExtensionInfo -MemberType AliasProperty -MemberName Total -Value TotalSize -Force

The function breaks down file extensions in a given folder and provides a summary.

PS C:\> Get-FileExtensionInfo d:\temp -Recurse

   Path: D:\temp [PROSPERO]

Extension Count TotalSize Smallest Average Largest
--------- ----- --------- -------- ------- -------
html          1       836      836     836     836
png           1    154085   154085  154085  154085
txt           2   2069090      290 1034545 2068800

The function also includes a custom format file which will highlight the extension with the largest total size.

Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.