我以为我对此有了答案,但我玩的越多,我就越认为它是Powershell的设计缺陷。
我想拖放(或使用Send-To机制)将多个文件和/或文件夹作为数组传递给Powershell脚本。
测试脚本
#Test.ps1
param ( [string[]] $Paths, [string] $ExampleParameter )
"Paths"
$Paths
"args"
$args
尝试#1
我使用以下命令行创建了一个快捷方式,并将一些文件拖到它上面。这些文件作为单独的参数出现,这些参数首先匹配脚本参数的位置,其余部分放在$ args数组中。
尝试#1的快捷方式
powershell.exe -noprofile -noexit -file c:\Test.ps1
包装脚本
我发现我可以用包装脚本来完成这个......
#TestWrapper.ps1
& .\Test.ps1 -Paths $args
包装脚本的快捷方式
powershell.exe -noprofile -noexit -file c:\TestWrapper.ps1
批处理文件包装程序脚本
它通过批处理文件包装脚本...
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%}"
尝试回答
Keith Hill使得优秀suggestion使用以下快捷命令行,但它没有正确传递参数。当到达Test.ps1脚本时,带有空格的路径被拆分。
powershell.exe -noprofile -noexit -command "& {c:\test1.ps1 $args}"
有没有人在没有额外脚本的情况下找到了这样做的方法?
答案 0 :(得分:26)
我意识到这个问题现在已经有几年了,但是因为它仍然是很多谷歌查询通过资源管理器拖放运行PowerShell的最佳结果我想我会发布我发现的内容今天希望它能帮助别人。
我希望能够将文件拖放到PowerShell(ps1)脚本上,并且找不到任何好的解决方案(没有任何不需要额外的脚本或快捷方式)。我开始在注册表中查找文件伙伴,我想出了一些似乎完美无缺的东西。
首先,我们需要为.PS1文件添加一个DropHandler条目,以便Explorer知道PS1文件应该接受拖放操作。使用此注册表更改执行此操作。您可能必须创建ShellEx和DropHandler子项。
HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\ShellEx\DropHandler\
(Default) = {60254CA5-953B-11CF-8C96-00AA00B8708C}
此下拉处理程序是Windows Scripting Host使用的处理程序。这是我的第一选择,因为它支持长文件名。如果您遇到空格或其他问题,可以尝试标准的Shell32可执行文件(.exe,.bat等)放置处理程序:{86C86720-42A0-1069-A2E8-08002B30309D}
。
这两个drop处理程序只需调用默认动词(双击文件时会发生什么)来处理文件类型(看作Shell键的(Default)
值)。对于PS1文件,这是打开。默认情况下,此动词在记事本中显示PowerShell脚本 - 不是很有帮助。
通过修改PS1文件的Open动词来更改此行为:
HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command\
(Default) = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoExit -File "%1" %*
这将在打开时在PowerShell中运行脚本,并将所有参数(删除的文件)传递给它。脚本完成后我将其设置为保持打开状态,但您可以通过删除-NoExit
选项来更改此设置。
就是这样。我没有做过任何广泛的测试,但到目前为止它似乎工作得很好。我可以删除单个文件/文件夹以及组。参数列表中文件的顺序并不总是您所期望的(资源管理器如何命令所选文件),但除此之外它似乎是理想的。您还可以在Shell:Sendto
中创建PS1文件的快捷方式,以便使用“发送至”菜单传递文件。
以下是REG文件格式的两种更改:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\ShellEx\DropHandler]
@="{60254CA5-953B-11CF-8C96-00AA00B8708C}"
[HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell\Open\Command]
@="\"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -NoExit -File \"%1\" %*"
注意:
由于这些更改,在资源管理器中双击PS1文件将执行脚本;要编辑而不是打开,您必须使用右键单击菜单。正如一个建议(遗憾地从苦涩的经历中学到:),如果您的脚本具有破坏性的行为,您可能会考虑确认/理智检查。
通过拖放操作运行的脚本将具有C:\Windows\system32\
的默认起始工作目录。另一个要小心的理由。
请记住,除非您使用签名脚本,否则需要更改Execution Policy(Set-ExecutionPolicy)。
如果您在Windows 10上调整了.PS1文件的处理,则需要删除将覆盖默认处理的注册表项HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ps1\UserChoice
。
答案 1 :(得分:10)
将文件或文件夹传递给Powershell脚本的最简单方法是包装脚本,如下所示:
@echo off
rem the name of the script is drive path name of the Parameter %0
rem (= the batch file) but with the extension ".ps1"
set PSScript=%~dpn0.ps1
set args=%1
:More
shift
if '%1'=='' goto Done
set args=%args%, %1
goto More
:Done
powershell.exe -NoExit -Command "& '%PSScript%' '%args%'"
你所要做的就是
制作.bat文件的副本
给它与脚本文件相同的名称,但扩展名为.bat
例如“hello.ps1”< - > “hello.bat”
将文件/文件夹放到批处理文件中,然后将它们传递给脚本。
示例脚本代码可能如下所示:
"hello world"
$args
$args.Gettype().Fullname
答案 2 :(得分:3)
我不确定为什么我没有早点想到这个...
这个秘密一直是因为它可用,所以它是参数属性“ValueFromRemainingArguments”。
<强>快捷方式强>
powershell.exe -noexit -file c:\Test.ps1
<强> Test.ps1 强>
param (
[Parameter(ValueFromRemainingArguments=$true)]
$Path
)
'Paths'
$Path
答案 3 :(得分:1)
将您的快捷方式更改为此并尝试:
powershell.exe -noprofile -noexit -command "& {c:\test1.ps1 $args}"
答案 4 :(得分:1)
我已在bug report网站上提交了Powershell Microsoft Connect。
答案 5 :(得分:0)
不幸的是,$args
会将您的参数拆分为空格,因此在包含它们的文件名上工作不正确。但是,如果没有外部包装脚本,您仍然可以在.ps1中处理$args
!
由于您在快捷方式上拖放的所有内容(或选择&#34;发送到&#34;)将作为空间连接的绝对路径列表传递到您的脚本,因此您可以解析{{1}对于使用正则表达式的所有绝对路径出现并将其传递给foreach:
$args
每个$pattern = '([a-zA-Z]:\\(((?![<>:"/\\|?*]).)+((?<![ .])\\)?)*)(\s|$)'
Select-String "$pattern" -input "$args" -AllMatches | Foreach {$_.matches} | ForEach-Object {
$path = "$($_.Groups.Groups[1].Value)"
# ...
}
恰好是一条绝对路径(包括那些内部有空格的路径)
绝对文件模式取自here。
我将它包装在括号中(以便将第一个组与实际路径相匹配)并在末尾添加了一个$path
组,这是&#34;空格或字符串结尾&#34;,因为快捷方式需要参数将与单个空格连接; $是匹配参数列表中最后一个路径出现所必需的。
答案 6 :(得分:0)
对于那些寻求将n拖放单个文件拖放到需要字符串参数的Powershell脚本中的快速解决方案的人,可以将其用作快捷方式:
powershell.exe -noprofile -noexit -command "& {.\myscript.ps1 ($args -join ' ')}"
如果您的文件路径具有多个连续的空格,它可能会中断,但这对我来说效果很好,因为我从未遇到过需要使用此技术的此类文件。
答案 7 :(得分:0)
上面由 Nathan Hartley 使用快捷方式文件发布的解决方案效果很好。 (见:stackoverflow.com/a/45838761/80161)
事实证明,您可以同样轻松地使用 .BAT 文件,获得相同的结果,使用任一方法调用相同的底层 .ps1 脚本文件。
以下是对这两种技术的总结。以下示例脚本已在 Windows 10 上使用 Windows PowerShell 5.1 (powershell.exe) 和 PowerShell 7.1 (pwsh.exe) 进行了测试。
文件和目录名称中的多个连续空格使用以下任一技术保留,这是上述其他一些解决方案的问题。 (Nathan 的解决方案在这方面效果很好。我在下面的摘要中包括了他的解决方案以及我的 .bat 文件解决方案。)
在快捷方式的属性中输入类似以下内容作为“目标”:
powershell.exe -noexit -File "C:\Users\User\Documents\PS Scripts\your script.ps1"
假设 PowerShell 程序在您的路径中,您也可以在将上述内容输入快捷方式时简单地输入 powershell 或 pwsh。当您保存或应用快捷方式时,它会自动将名称扩展为完整的绝对文件名。此外,如果 -File 参数路径和文件名没有空格,您可以省略被调用的 .ps1 脚本名称周围的引号。
@echo off
set PSScript=%~dpn0.ps1
powershell.exe -NoExit -File "%PSScript%" %*
在上面另一位作者的帖子中,为 .BAT 文件提供与相应 PowerShell 脚本相同的基本名称。当然,如果您愿意,您当然也可以将被调用的脚本名称明确地编码到 .BAT 文件中,但以上内容可以方便地将包装器和 .ps1 脚本成对移动。
“%PSScript%”周围的引号很重要,但 %*(将传递给 .BAT 脚本的所有参数传递给 PowerShell 脚本)必须不要括在引号中。
使用“ValueFromRemainingArguments”规范在“named”参数中收集传递的文件名(这基本上是 Nathan 上面的示例):
param (
[Parameter(ValueFromRemainingArguments=$true)]
$Paths
)
'Paths:'
$Paths
在 $Args 数组中选取参数:
# Simply reference the dropped file names in the $Args array.
# The following will list all the dropped file names.
# Yes, this is a one line .ps1 script!
$Args
以下演示了显式访问单个传递的文件名:
$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])"
}
更新 6/2021:有关增强的代码,请参阅下面的 follow-up post,它也允许您包含已删除的目录。
注意事项:
Nathan 示例中显示的 [CmdletBinding()] 规范不是 必要并且被覆盖 [ValueFromRemainingArguments=$true] 规范,这导致所有 剩余的丢弃的文件名尚未被任何先前的 参数被收集到命名的 $Paths 参数中 附规格书。
在通过上面示例中的默认 $Args 变量访问删除的文件名时,不得使用 [CmdletBinding()] 规范,否则您将收到错误,因为此规范要求所有传递的参数都具有匹配参数规格。
如果 PowerShell 脚本中未指定“[CmdletBinding()]”和“[Parameter(ValueFromRemainingArguments=$true)]”,并且删除并传递给 PowerShell 脚本的文件多于命名参数,文件名首先通过命名参数传递,任何剩余的文件名作为 $Args 参数传递,以 $Args[0] 开头。
上述解决方案中的一个关键元素是在调用相应 . ps1 脚本。
如上所述,可以在快捷方式文件或批处理文件中使用 pwsh.exe (PowerShell 7) 代替 powershell.exe (Windows PowerShell) .
答案 8 :(得分:0)
这是我上面发帖的后续内容:“Dragging and Dropping Files onto a PowerShell Script --- Additional Information and Solutions”。
下面的 PowerShell 脚本可以与我之前发布的文章中描述的 .bat 文件或快捷方式文件技术一起使用,并为在 Windows 资源管理器中放置文件提供额外的灵活性。也可以在 PowerShell 命令提示符下使用该脚本。
这将允许删除目录和文件名的混合列表。目录将被扩展。
从命令行手动运行时,通配符也可用于指定文件(但不能用于目录)。
任何传递的相对文件名都将转换为完全限定名称,这对某些应用程序可能很重要。
param (
[Parameter(ValueFromRemainingArguments=$true)]
$DroppedFiles
)
if ( $DroppedFiles.count -eq 0 ) {
Write-Host "Supply directory and/or file names. Wildcards are allowed." -ForegroundColor Red
Exit
}
[bool]$err = $False
foreach ($f in $DroppedFiles) {
if ( ( [string]::IsNullOrWhiteSpace($f) ) -or ( (Test-Path -Path $f) -eq $False ) ) {
Write-host "ERROR--File or directory does not exist: " -NoNewline -ForegroundColor Red
Write-host $f
$err = $True
}
}
if ( $err ) { Exit }
$Droppedfiles = Get-ChildItem -Path $DroppedFiles -File -Depth 0 -Recurse -ErrorAction Stop
$i = 0
foreach ($File in $DroppedFiles) {
Write-Host "Processing:" $File.FullName
# ".FullName" was required to display the full absolute path name in Windows Powershell 5.1
$i++
}
Write-Output "$i file(s) processed."
注意事项:
这将处理多个混合的目录和文件名。
目录默认仅扩展到第一级(由“-Depth 0”设置)。您可以增加此项或完全取消限制。
您可以通过向 Get-ChildItem 行添加 -Include 参数来限制生成的文件列表,例如:-Include: *.doc,*.docx
从命令行调用脚本时,任何指定的不存在的文件都不返回任何内容(不报告错误),因此包括对不存在的文件或目录的初步检查。
当脚本由 Windows PowerShell 5.1 运行时,需要在最终的 foreach 循环中使用 $File.FullName 规范来引用单个已删除的文件名以显示完整的文件名。
编写的脚本适用于 Windows PowerShell 5.1 (powershell.exe) 和 PowerShell 7.1 (pwsh.exe)。
附加评论
答案 9 :(得分:0)
为了简化我希望能够删除文件和目录的多个 PowerShell 脚本的创建,我创建了一个函数来执行检查和目录扩展。
>以下是我想出的:
<#
Function: Test+Expand-Dropped-Files
Lewis Newton, 6/2021
This function will process an intermixed list of directory and file names passed to a
PowerShell script when dropped in Windows Explorer on a properly configured accompanying
Windows shortcut or .bat file. The PowerShell script or .bat file can also be called directly
from the command line.
-- This function will accept multiple intermixed directory and file names.
-- It will check that the files exist, and expand any directories recursively to the depth
specified (it defaults to 0, the top level).
-- The 'Include' parameter allows limiting what is included, for example: -Include *.doc,*.docx
It defaults to a $Null string array which will include everything found.
-- Relative file names and wildcard characters are accepted.
-- The function returns fully qualified file objects.
-- This has been tested with Windows PowerShell 5.1 and PowerShell 7.1.
In using the results, note that ".FullName" was required to display
the full absolute path name in Windows Powershell 5.1.
#>
function Test+Expand-Dropped-Files {
param(
$DroppedFiles,
[string]$Depth = "0",
[string[]]$Include = $Null
)
if ( $DroppedFiles.count -eq 0 ) {
Write-Host "Supply directory and/or file names. Wildcards are allowed." -ForegroundColor Red
Exit
# Makes sure there is at least one parameter, or Get-ChildItem defaults to all in current directory.
}
[bool]$err = $False
foreach ($f in $DroppedFiles) {
if ( ( [string]::IsNullOrWhiteSpace($f) ) -or ( (Test-Path -Path $f) -eq $False ) ) {
Write-host "ERROR--File or directory does not exist: " -NoNewline -ForegroundColor Red
Write-host $f
$err = $True
}
}
if ( $err ) { Exit }
$Droppedfiles = Get-ChildItem -Path $DroppedFiles -File -Recurse -Depth $Depth -Include $Include -ErrorAction Stop
# '-Depth 0' limits expansion of files in directories to the top level.
# '-Include $Null' will include everything.
Write-Output $DroppedFiles
}
我将上述内容放在名为“function_Test+Expand-Dropped-Files.ps1”的文件中,从系统路径中的目录中点源它,并从我的工作 PowerShell 脚本中调用该函数,如下所示:
param ( [Parameter(ValueFromRemainingArguments=$true)] $DroppedFiles )
. function_Test+Expand-Dropped-Files
$DroppedFiles = Test+Expand-Dropped-Files $DroppedFiles -Depth 0 -Include $Null
# '-Depth 0' limits directory expansion to first level.
# '-Include' can specify a string array of specifications, such as: -Include *.doc,*.docx
# '-Depth' and '-Include' are optional and default to 0 and $Null (includes all) respectively.
#Your specific code to process each file goes within the following loop:
$i = 0
foreach ($File in $DroppedFiles) {
Write-Host "Processing:" $File.FullName
$i++
}
Write-Output "$i file(s) processed.`n"
然后,您可以将文件和/或目录拖放到以下 .bat 文件(为 .bat 文件指定与工作 .ps1 脚本相同的基本名称):
@echo off
set PSScript=%~dpn0.ps1
powershell.exe -NoLogo -NoExit -File "%PSScript%" %*
或者,您可以将文件放置在之前帖子中描述的类似快捷方式文件中。
上述功能点源的另一种选择是将“Test+Expand-Dropped-Files”功能放在您的 PowerShell 配置文件中或将其设置为 PowerShell 模块。 >