Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell function scoping issue

I am writing a Powershell script that wraps around ffmpeg to convert videos from mpeg-4 to h.265 in order to save space on my hard drive. I am trying to write a helper function that will use ffprobe to parse information about the video stream's codec to validate that the file the user has provided is an mpeg file; it's called isMpeg. It should take the user provided input as passed by reference and return true or false. When I run the code, I get the following error message:

C:\tmp\ps1\Format-ToHEVC.ps1 : Cannot validate argument on parameter 'Source'. The term 'isMpeg' is not recognized as the name of a cmdlet, function, script file, or operable 
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:21
+ .\Format-ToHEVC.ps1 ".\Format-ToHEVC.ps1"
+                     ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Format-ToHEVC.ps1], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Format-ToHEVC.ps1

Could somebody help me understand the problem I'm having? Seems like a scoping issue, but I don't really understand the why PowerShell isn't seeing the helper function I created in it's scope during the validation check. Combing through documentation hasn't really helped me understand the issue. If there is a better way to do this validation, I could use some suggestions. Please help!!

Code:

<# Format-ToHEVC.ps1
    .SYNOPSIS
        This function will re-encode standard MPEG-4/H.264 encoded files to HEVC/H.265 at same video resolution.
    .DESCRIPTION
        This function will re-encode standard MPEG-4/H.264 encoded files to HEVC/H.265 at same video resolution.
        Requires ffmpeg to be installed on the system. Tested with ffmpeg 5.1.2.
    .NOTES
        This function requires ffmpeg to be installed on the system and available from the system path. It was tested using ffmpeg version 5.1.2.
        Prior versions will probably work, but have not been verified. The version you use should support libx265 for HVEC. This should be obvious
        as that is the purpose of the function. FFProbe is also required and should be installed along with ffmpeg.
    .EXAMPLE
        Format-ToHVEC -Source original.mp4 -Dest newfile.mp4
        This will do what you expect. It converts a MPEG-4/H.264 formatted video file to HEVC/H.265.
    .EXAMPLE
        Format-ToHVEC -basePath C:\Videos [-MaxDepth 5]
        This will recursively search a folder structure for video files to be converted using batch processing. FolderLevel is 1 level deep by default. 
        This can be specified using the -MaxDepth parameter. A MaxDepth above 5 will be ignored and set to the max level of 5. 
    #>

    [CmdletBinding()]
    param (
    [ValidateScript({
        if(-Not ($_ | Test-Path))
        {
            throw "Invalid file or folder specified error. Please provide another source file or folder."
        }
        elseif(isMpeg $Source) { return $true }
        else {
            throw "Invalid File Type Error. Requires file be mpeg-4/h.264."
        }
        
        
    })]
    [System.IO.FileInfo]$Source
    )    
function isMpeg([ref]$Source)
{
    Write-Debug("Success")
}

I tried moving the helper function inside the ValidateScript block and it still gave me the same error. I tried moving the helper function to the top of the file, but since [cmdletbinding()] isn't the first line, it threw an exception complaining about that.

like image 637
user3391002 Avatar asked Sep 08 '26 16:09

user3391002


1 Answers

Could somebody help me understand the problem I'm having? [...] PowerShell isn't seeing the helper function I created in it's scope during the validation check.

PowerShell's function statement is treated the same as any other statement and evaluated sequentially. This means the local-scoped function isMpeg only starts "existing" after parameter binding has completed and the script body is invoked.

This behavior differs significantly from languages like JavaScript or Python, where function declarations in the same lexical scope are all registered (or "hoisted") prior to runtime.

The preferred solution to this problem is to write modules rather than standalone scripts, as all the functions in a module share a common lexical scope, making reuse of helper functions viable.


For single-file scripts, PowerShell has another workaround - classes!

Type definitions using the class or enum keywords are not only parsed, but fully compiled, before parameter binding occurs.

[CmdletBinding()]
param (
    [ValidateScript({
        # [MpegUtility] is already available
        if([MpegUtility]::isMpeg($_)) { 
            return $true
        }
        else {
            throw "Invalid File Type Error. Requires file be mpeg-4/h.264."
        }
    })]
    [System.IO.FileInfo]$Source
)

# the class definition is down here at the bottom, but is compiled before anything else executes!
class MpegUtility {
    static [bool] isMpeg([System.IO.FileInfo]$file) {
        return $file.Extension -match '\.mp(?:4|e?g)'
    }
}
like image 51
Mathias R. Jessen Avatar answered Sep 11 '26 20:09

Mathias R. Jessen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!