Powershell 函数变量的范围

Powershell 函数变量的范围,powershell,Powershell,我需要递归地计算mp3和flac文件的数量。这是我的密码: function GetStat() { $mp3_count = 0 $flac_count = 0 Set-Variable -Name mp3_count,flac_count -Option AllScope function getFiles($path) { foreach ($item in Get-ChildItem $path)

我需要递归地计算
mp3
flac
文件的数量。这是我的密码:

function GetStat() 
{
    $mp3_count = 0 
    $flac_count = 0
    Set-Variable -Name mp3_count,flac_count -Option AllScope

    function getFiles($path) 
    {     
        foreach ($item in Get-ChildItem $path)
        {
            if (Test-Path $item.FullName -PathType Container) 
            {

                GetFiles $item.FullName
            } 
            else 
            { 
                if ($item.Extension -eq ".mp3")
                {                    
                    $mp3_count = $mp3_count + 1                 
                }

                if ($item.Extension -eq ".flac")
                {
                    $flac_count = $flac_count + 1                   
                }
            }
        } 
    }

    getFiles "Y:\music (flac)\Alternative"
    $mp3_count
    $flac_count
}
我的函数写入
0,0
,它看起来像
$mp3\u count
flac\u count
变量在每次调用函数
getFiles()
时重置


如何修复代码?

一般来说,除非绝对需要,否则不要触及全局范围。函数中有一大堆额外的东西,包括一个不必要的嵌套函数。这是做同样事情的更实际的方法:

# Functions should be named Verb-<Whatever>
function Get-MediaStats ($Path) 
{
    # Everything that was here was extra and unneeded.
    foreach ($item in Get-ChildItem $path -Recurse)
    {
        if ($item.Extension -eq ".mp3")
        {                    
            $mp3_count++                 
        }

        if ($item.Extension -eq ".flac")
        {
            $flac_count++               
        }
    } 

    # This creates an object... The 'return' or 'write-output' is implied.
    [PSCustomObject]@{
        'MP3Count' = $mp3_count
        'FLACCount' = $flac_count
    }

}

# Instead of nested functions you can call the function like so... 
# Because it's returning a an object the variable $Result will contain the output.
$Results = Get-MediaStats C:\Path\To\Files
$Results

如果您执行了
$Result=返回某些内容

这些操作中的任何一个都可以工作,您只需使用
Get ChildItem-Recurse
即可跳过所有这些操作。但是如果您想自己实现递归,这里有几个指针

我建议不要在局部范围之外写入变量

让递归函数返回哈希表,然后将每个递归的值添加到本地哈希表:

function Get-FileCount 
{
    param(
        [Parameter(Mandatory=$false)]
        [ValidateScript({Test-Path -LiteralPath $_ -PathType Container})]
        [string]$Path = (Resolve-Path $PWD),

        [Parameter(Mandatory=$false)]
        [string[]]$Extensions = @('flac','mp3')
    )

    # build local table to hold counts per extension
    $ExtensionCount = @{}

    # add dot to extensions if missing
    $Extensions = $Extensions |ForEach-Object {
        $_ -replace '^(?=[^\.])','.'
    }

    # retrieve all items at current $Path
    Get-ChildItem -LiteralPath $Path |ForEach-Object {
        # recurse if directory
        if($_ -is [System.IO.DirectoryInfo]){
            # retrieve count from subfolder
            $Subcount = Get-FileCount -Path $_.FullName -Extensions $Extensions
            foreach($Extension in $Subcount.Keys){
                # add results from recursive call for each identified extension to current count
                $ExtensionCount[$Extension] += $Subcount[$Extension]
            }
        } 
        elseif($_.Extension -in $Extensions) {
            # increment count for the found extension in local $Path
            $ExtensionCount[$_.Extension]++
        }
    }

    # return the counting table
    return $ExtensionCount
}
现在,您可以使用以下命令检索每种类型的计数:

Get-FileCount "Y:\music (flac)\Alternative"
如果要添加新的文件类型,则无需重写任何内容:

Get-FileCount "Y:\music (flac)\Alternative" -Extensions flac,mp3,avi,ogg

我将使用
$items=get ChildItem-Recurse-File-Filter“*.mp3”-Path$pathToQuery
递归地获取所有文件。然后用户
$items.Count
。您可以使用多个扩展名优化一次扫描,然后使用自定义推理在使用
Where Object
进行计数筛选之前进行优化。比如说

$items=Get-ChildItem -Recurse -File -Filter @("*.mp3","*.flac") -Path $pathToQuery
($items|Where-Object {#filter1 reasoning}).Count
($items|Where-Object {#filter2 reasoning}).Count

Mathias R.Jessen已经提供了一个很好的答案,但对于一些简单的问题,您可以使用带有
-Recurse
参数的
Get ChildItem
,并将其传递到
Group Object
以使其变得更简单

Function Get-FileCount{
Param(
    [string]$Path,
    [string[]]$Extensions = ".MP3",".FLAC"
)
    # Sanitize extensions
    $Extensions = $Extensions -replace "^(?:.*\.)?([^\.]+)$",'.$1'
    #Locate, filter, and group files
    Get-ChildItem $Path -Recurse | Where{ $_.Extension -in $extensions } | Group Extension | Select Name,Count
}
然后你可以这样称呼它:

Get-FileCount "Y:\music (flac)\Alternative"
它将输出如下内容:

Name    Count
----    -----
.mp3    37
.flac   163

您可以将所有出现的
$mp3\u count
替换为
$global:mp3\u count
,以便始终在全局范围内读取和写入同一变量。您可以将变量创建为全局变量,但稍后将其设置为未定义(函数中的局部变量)。如上所述,使用范围修饰符。此外,您还可以将所有这些替换为
$files=dir-path“Y:\music(flac)\Alternative”-file-recurse$mp3_count=@($files | where{$\扩展名-match'mp3$).count$flac|u count=@($files{$\.extension-match'flac$).count
$flac|count,$mp3|count=dir“Y:\music(flac)\Alternative”-file-recurse{$\.extension-match'\.flac$\.mp3$”}组扩展名|排序名|选择-展开计数
Name    Count
----    -----
.mp3    37
.flac   163