Powershell 从ForEach对象内部递归调用函数-并行块-在并行块内部无法识别函数
第一次来这里。请善待我:) 我试图以并行方式递归地获取所有目录,希望减少遍历驱动器所需的时间。下面是我尝试过的代码。基本上,我要做的是输入一个文件夹,并对它的子文件夹及其子文件夹进行并行处理,等等,但是在并行块中无法识别该函数Powershell 从ForEach对象内部递归调用函数-并行块-在并行块内部无法识别函数,powershell,Powershell,第一次来这里。请善待我:) 我试图以并行方式递归地获取所有目录,希望减少遍历驱动器所需的时间。下面是我尝试过的代码。基本上,我要做的是输入一个文件夹,并对它的子文件夹及其子文件夹进行并行处理,等等,但是在并行块中无法识别该函数 function New-RecursiveDirectoryList { [CmdletBinding()] param ( # Specifies a path to one or more locations. [Pa
function New-RecursiveDirectoryList {
[CmdletBinding()]
param (
# Specifies a path to one or more locations.
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Path to one or more locations.')]
[Alias('PSPath')]
[ValidateNotNullOrEmpty()]
[string[]]
$Path
)
process {
foreach ($aPath in $Path) {
Get-Item $aPath
Get-ChildItem -Path $aPath -Directory |
# Recursively call itself in Parallel block not working
# Getting error "The term 'New-RecursiveDirectoryList' is not recognized as a name of a cmdlet"
# Without -Parallel switch this works as expected
ForEach-Object -Parallel {
$_ | New-RecursiveDirectoryList
}
}
}
}
错误:
New-RecursiveDirectoryList:
Line |
2 | $_ | New-RecursiveDirectoryList
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
| The term 'New-RecursiveDirectoryList' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
我还尝试使用mklement0提供的解决方案,但没有成功。以下是我的尝试:
function CustomFunction {
[CmdletBinding()]
param (
# Specifies a path to one or more locations.
[Parameter(Mandatory = $true,
Position = 0,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Path to one or more locations.')]
[Alias('PSPath')]
[ValidateNotNullOrEmpty()]
[string[]]
$Path
)
begin {
# Get the function's definition *as a string*
$funcDef = $function:CustomFunction.ToString()
}
process {
foreach ($aPath in $Path) {
Get-Item $aPath
Get-ChildItem -Path $aPath -Directory |
# Recursively call itself in Parallel block not working
# Getting error "The term 'New-RecursiveDirectoryList' is not recognized as a name of a cmdlet"
# Without -Parallel switch this works as expected
ForEach-Object -Parallel {
$function:CustomFunction = $using:funcDef
$_ | CustomFuction
}
}
}
}
错误
有人知道这是如何实现的吗?或者是一种不同的实现方法吗?我看到您编写了一个强制参数,这意味着您需要在运行函数时调用它。例如,在您的情况下,您可以尝试在内存中手动运行选定的行。要执行此操作,请打开PowerShell会话,然后简单地复制/粘贴您在此处发布的代码。一旦代码加载到内存中,就可以调用函数:
CustomFunction -Path "TheTargetPathYouWant"
所以,这对我来说很有效,它看起来很不漂亮。 需要注意的是,脚本上的
foreach($Path中的aPath){…}
是不必要的,当您通过多个路径时,process{…}
块将为您处理该问题
代码:
结果如下所示:
Nesting Path
------- ----
0 /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
2 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
2 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
3 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
3 |_ /home/user/Documents/...
3 |_ /home/user/Documents/...
4 |_ /home/user/Documents/...
5 |_ /home/user/Documents/...
我当时也做过类似的事情。我使用了非递归函数,但使用了来自DotNet的运行空间。为此,您需要安装PoshRsJob模块并创建一个要在dir.txt中提取的子文件夹列表。然后运行以下命令:
Install-Module PoshRsJob -Scope CurrentUser
function ParallelDir {
param (
$Folders,
$Throttle = 8
)
$batch = 'ParallelDir'
$jobs = Get-RSJob -Batch $batch
if ($jobs | Where-Object State -eq 'Running') {
Write-Warning ("Some jobs are still running. Stop them before running this job.
> Stop-RSJob -Batch $batch")
return
}
$Folders | Start-RSJob -Throttle $Throttle -Batch $batch -ScriptBlock {
Param ($fullname)
$name = Split-Path -Path $fullname -Leaf
Get-ChildItem $fullname -Recurse | Select-Object * | Export-Clixml ('c:\temp\{0}.xml' -f $name)
} | Wait-RSJob -ShowProgress | Receive-RSJob
if (!(Get-RSJob -Batch $batch | Where-Object {$_.HasErrors -and $_.Completed})) {
Remove-RSJob -Batch $batch
} else {
Write-Warning ("The copy process has finished with ERROR. You can check:
> Get-RsJob -Batch $batch
To consolidate the results from each copy run:
> Get-ChildItem 'c:\temp\*.xml' | Import-Clixml")
}
}
$dir = gc .\dir.txt
ParallelDir -Folders $dir
dir c:\temp\*.xml|Import-Clixml|select name,length
谢谢你的回答,尽管我认为这不是问题所在。正在从管道传递强制路径参数。问题是函数本身无法从错误中显示的并行脚本块中调用。我想知道是否有一种方法可以将函数定义传递到并行块中,这样就可以使用它了。啊,这非常有效。。也很可怕:)但这不是你的错。我想,为每个文件夹生成所有这些进程并不是很有效。我真的很喜欢嵌套级别的第二个版本。手感很好。我自己也在玩scriptblock,但没弄明白<代码>$scriptblock=$MyInvocation.MyCommand.scriptblock.ToString()是我缺少的部分。谢谢你分享这个并解决我的问题。很高兴知道如何将调用函数引入并行块以供将来参考。@Daniel查看我上次的编辑,你的问题是一个很棒的练习,所以谢谢:)顺便说一下,我想补充一下,如果你想在一个非常大的共享/驱动器上递归地运行它,你可能会想在你的
foreach对象中添加一个-ThrottleLimit
,Daniel Hierarchy在Linux tho上看起来不是很好,但是如果你想看看广告组的真正功能是如何工作的,请检查我的
function Test {
[CmdletBinding()]
param (
# Specifies a path to one or more locations.
[Parameter(Mandatory)]
[Alias('PSPath')]
[string[]]$Path,
[int]$Nesting = 0
)
begin {
$scriptblock = $MyInvocation.MyCommand.ScriptBlock.ToString()
}
process {
Get-ChildItem -Path $Path -Directory | ForEach-Object -Parallel {
function Indent{
param(
[String]$String,
[Int]$Indent
)
$x='_';$y='|';$z=' '
switch($Indent)
{
{$_ -eq 0}{return $String}
{$_ -gt 0}{return "$($z*$_)$y$x $string"}
}
}
$z = $using:Nesting
[pscustomobject]@{
Nesting = $z
Path = Indent -String $_.FullName -Indent $z
}
$z++
$i = $using:scriptblock
$thisIsNotPretty = [scriptblock]::Create($i)
& $thisIsNotPretty -Path $_ -Nesting $z
}
}
}
[System.Collections.ArrayList]$result = Test -Path /home/user/Documents
function Draw-Hierarchy{
param(
[System.Collections.ArrayList]$Array
)
$Array.Reverse()
for($i=0;$i -lt $Array.Count;$i++){
if(
$Array[$i+1] -and
$Array[$i].Path.IndexOf('|_') -lt $Array[$i+1].Path.IndexOf('|_')
){
$z=$i+1
$ind=$Array[$i].Path.IndexOf('|_')
while($Array[$z].Path[$ind] -ne '|'){
$string=($Array[$z].Path).ToCharArray()
$string[$ind]='|'
$string=$string -join ''
$Array[$z].Path=$string
$z++
if($Array[$z].Path[$ind] -eq '|'){break}
}
}
}
$Array.Reverse()
return $Array
}
Draw-Hierarchy $result
Nesting Path
------- ----
0 /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
2 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
2 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
3 | |_ /home/user/Documents/...
1 |_ /home/user/Documents/...
3 |_ /home/user/Documents/...
3 |_ /home/user/Documents/...
4 |_ /home/user/Documents/...
5 |_ /home/user/Documents/...
Install-Module PoshRsJob -Scope CurrentUser
function ParallelDir {
param (
$Folders,
$Throttle = 8
)
$batch = 'ParallelDir'
$jobs = Get-RSJob -Batch $batch
if ($jobs | Where-Object State -eq 'Running') {
Write-Warning ("Some jobs are still running. Stop them before running this job.
> Stop-RSJob -Batch $batch")
return
}
$Folders | Start-RSJob -Throttle $Throttle -Batch $batch -ScriptBlock {
Param ($fullname)
$name = Split-Path -Path $fullname -Leaf
Get-ChildItem $fullname -Recurse | Select-Object * | Export-Clixml ('c:\temp\{0}.xml' -f $name)
} | Wait-RSJob -ShowProgress | Receive-RSJob
if (!(Get-RSJob -Batch $batch | Where-Object {$_.HasErrors -and $_.Completed})) {
Remove-RSJob -Batch $batch
} else {
Write-Warning ("The copy process has finished with ERROR. You can check:
> Get-RsJob -Batch $batch
To consolidate the results from each copy run:
> Get-ChildItem 'c:\temp\*.xml' | Import-Clixml")
}
}
$dir = gc .\dir.txt
ParallelDir -Folders $dir
dir c:\temp\*.xml|Import-Clixml|select name,length