Search code examples
powershell

Drag and Drop to a Powershell script


I thought I had an answer to this, but the more I play with it, the more I see it as a design flaw of Powershell.

I would like to drag and drop (or use the Send-To mechanism) to pass multiple files and/or folders as a array to a Powershell script.

Test Script

#Test.ps1
param ( [string[]] $Paths, [string] $ExampleParameter )
"Paths"
$Paths
"args"
$args

Attempt #1

I created a shortcut with the following command line and dragged some files on to it. The files come across as individual parameters which first match the script parameters positionally, with the remainder being placed in the $args array.

Shortcut for Attempt #1

powershell.exe -noprofile -noexit -file c:\Test.ps1

Wrapper Script

I found that I can do this with a wrapper script...

#TestWrapper.ps1
& .\Test.ps1 -Paths $args

Shortcut for Wrapper Script

powershell.exe -noprofile -noexit -file c:\TestWrapper.ps1

Batch File Wrapper Script

And it works through a batch file wrapper script...

REM TestWrapper.bat
SET args='%1'
:More
SHIFT
IF '%1' == '' GOTO Done
SET args=%args%,'%1'
GOTO More
:Done
Powershell.exe -noprofile -noexit -command "& {c:\test.ps1 %args%}"

Attempted Answers

Keith Hill made the excellent suggestion to use the following shortcut command line, however it did not pass the arguments correctly. Paths with spaces were split apart when they arrived at the Test.ps1 script.

powershell.exe -noprofile -noexit -command "& {c:\test1.ps1 $args}"

Has anyone found a way to do this without the extra script?


Solution

  • The secret all this time, since it was made available, has been the parameter attribute "ValueFromRemainingArguments".

    Shortcut, Batch or CMD file to receive the dropped files

    Version 5.1 or earlier

    powershell.exe -noexit -file c:\Test.ps1
    

    Powershell 6 and newer

    pwsh.exe -noexit -file c:\Test.ps1
    

    Test.ps1

    [CmdletBinding()]
    param (
        [Parameter(ValueFromRemainingArguments=$true)]
        $Path
    )
    
    $Path
    

    TestExpandDirectories.ps1

    [CmdletBinding()]
    param (
        [Parameter(ValueFromRemainingArguments=$true)]
        $Path
    )
    
    foreach ($aPath in $Path) {
        $aPath
        if (Test-Path $aPath -PathType Container) {
            Get-ChildItem $aPath -Recurse | Select-Object -ExpandProperty FullName
        }
    }
    

    Also, if the script does not have a param block and is ran with Powershell -File ScriptName.ps1, the $args variable will contain exactly what is expected, an array of file paths.