Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/powershell/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell 从ForEach对象内部递归调用函数-并行块-在并行块内部无法识别函数_Powershell - Fatal编程技术网

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