Dragging and Dropping Files onto a PowerShell Script --- Additional Information and Solutions
The solution posted above by Nathan Hartley using a shortcut file works well. (See: stackoverflow.com/a/45838761/80161)
It turns out that you can just as easily use a .BAT file, with the same results, using either to call the same underlying .ps1 script files.
Below is a summary of the two techniques. The sample scripts below have been tested on Windows 10 using both Windows PowerShell 5.1 (powershell.exe) and PowerShell 7.1 (pwsh.exe).
Multiple consecutive spaces in file and directory names are preserved with either technique below, which was a problem with some of the other above mentioned solutions. (Nathan's solution works well in this regard. I include his solution, along with my .bat file solution, in my summary below.)
Sample Shortcut file:
Enter something like the following as the "Target" in the Shortcut's properties:
powershell.exe -noexit -File "C:\Users\User\Documents\PS Scripts\your script.ps1"
You can also simply type powershell or pwsh when entering the above into the Shortcut, assuming the PowerShell program is in your path. When you Save or Apply the Shortcut it will automatically expand the name to a full absolute file name. Also, if the -File parameter path and filename have no spaces you can omit the quotation marks around the called .ps1 script name.
Sample .BAT file:
@echo off
set PSScript=%~dpn0.ps1
powershell.exe -NoExit -File "%PSScript%" %*
As in the posting by another author above, give the .BAT file the same base name as the corresponding PowerShell script. Of course you can, instead, expressly code the called script name into the .BAT file if you prefer, but the above makes it convenient to move the wrapper and .ps1 scripts around as a pair.
The quotes around "%PSScript%" are important, but %* (which passes all parameters passed to the .BAT script to the PowerShell script) must not be enclosed in quotes.
The below example .ps1 scripts can be called by either of the above:
Option 1: Collecting passed path names in a 'named' parameter using the 'ValueFromRemainingArguments' specification (this is basically Nathan's example above):
param (
[Parameter(ValueFromRemainingArguments=$true)]
$Paths
)
'Paths:'
$Paths # lists the dropped path names
Option 2: Picking up parameters in the $Args array:
# Simply reference the dropped path names in the $Args array.
# The following will list all the dropped path names.
# Yes, this is a one line .ps1 script!
$Args
The following demonstrates "Option 2" with some added simple code accessing passed path names individually:
$Args
''
'Example of accessing passed Args individually:'
write-host "=====There are a total of $($args.count) arguments:"
for ( $i = 0; $i -lt $args.count; $i++ ) {
write-host "Argument $i is $($args[$i])"
}
Update 6/2021: See follow-up post below for enhanced code that will allow you to include and expand dropped directories as well.
Update 10/2021: Use of either the Windows shortcut approach or the .bat file approach generally works well. However note the following tradeoffs: The .bat file is super easy to set up for a new script--just make a copy of a previous one and give it the same base name as your new .ps1 script. On the other hand I found that you can drop more files on the Windows shortcut. As an example I found, in one of my comparison tests, I could drop about 90 files on my .bat file and about 380 files on my shortcut. Of course if the files are in a directory, and you drop the directory name, then you avoid the limitation altogether.
Notes about the code options shared above:
The [CmdletBinding()] specification shown in Nathan's example is not
necessary and is overridden by the
[ValueFromRemainingArguments=$true] specification, which causes all
remaining dropped file names not already picked up by any preceding
parameters to be gathered into the named $Paths parameter to which
the specification is attached.
The [CmdletBinding()] specification must not be used when accessing the dropped file names via the default $Args variable in the example above, otherwise you'll get an error since this specification demands the all passed parameters have matching parameter specifications.
If neither “[CmdletBinding()]” nor “[Parameter(ValueFromRemainingArguments=$true)]” are specified in the PowerShell script and more files are dropped and passed to the PowerShell script than there are named parameters, the file names are passed first via the named parameters, and any remaining file names are pass as $Args parameters, starting with $Args[0].
A key element in the solutions above is the use of the -File parameter rather than the -Command parameter in the PowerShell command that calls the respective .ps1 script.
As mentioned above, pwsh.exe (PowerShell 7) can be used instead of powershell.exe (Windows PowerShell) in the Shortcut file or Batch file.