将ValidateScript与稍后在脚本中定义的自定义函数结合使用

问题描述:

是否可以使用自定义函数和ValidateScript,其中函数稍后在脚本中定义。 此外,调用此函数时是否可以引用其他参数(即假设没有循环依赖)?将ValidateScript与稍后在脚本中定义的自定义函数结合使用

我明白为什么这可能是不可能的,但因为它是有用的东西我希望MS实施了一些特殊的规则,以允许脚本被读取和函数定义在发生参数验证之前可用。

例如

#Run-DemoScript.ps1 
param (
    [Parameter(Mandatory = $true)] 
    [string]$DbInstance 
    , 
    [Parameter(Mandatory = $true)] 
    [string]$DbCatalog 
    , 
    [Parameter(Mandatory = $true)] 

    # 
    # Is this possible; i.e. 
    # - Validate-Country is not defined until later in this script 
    # - DbInstance and DbCatalog parameters are defined alongside Country 
    [ValidateScript({Validate-Country -Country $_ -DbInstance $DbInstance -DbCatalog $DbCatalog})] 
    # 


    [string]$Country 
) 

#returns $true if the country is in the database's country table; otherwise false 
function Validate-Country { 
    [CmdletBinding()] 
    param (
     [Parameter(Mandatory = $true)] 
     [string]$DbInstance 
     , 
     [Parameter(Mandatory = $true)] 
     [string]$DbCatalog 
     , 
     [Parameter(Mandatory = $true)] 
     [string]$Country 
    ) 
    process { 
     $Country = $Country -replace "'","''" 
     ((Execute-SQLQuery -DbInstance $DbInstance -DbCatalog $DbCatalog -Query "select top 1 1 x from dbo.Country where Name = '$Country'") | Measure-Object | Select -ExpandProperty Count) -gt 0 
    } 
} 

function Execute-SQLQuery { 
    #... 
} 

"Script ran with Country $Country" 

更新

看来你可以将整个函数定义移动到ValidateScript属性,而且还可以在以后访问该功能的脚本;例如:

param(
    [Parameter(Mandatory = $true)] 
    [ValidateScript({ 
     function IsValid ($test) { 
      $test -eq 'test' 
     } 
     IsValid $_ 
    })] 
    [string]$x 
) 


"Output: $x" 
"Is Valid? $(IsValid $x)" 

然而,这是相当痛苦的。此外,它不允许引用同级的参数(例如下面)

param(
    [Parameter(Mandatory = $true)] 
    [string]$y 
    , 
    [Parameter(Mandatory = $true)] 
    [ValidateScript({ 
     function IsValid ($a,$b) { 
      $a -eq $b 
     } 
     IsValid $_, $y 
    })] 
    [string]$x 
) 


"X: $x" 
"Y: $Y" 
"Is Valid? $(IsValid $x $y)" 
+2

我确定这已经发生在你身上了,但为什么不只是让'Validate-Country'早些时候在另一个“主”脚本中找到?尽管为了保持这一点,我完全理解你的请求的必要性。 – Matt

+0

谢谢@Matt;是的,这是一个好的解决方案;我只是希望将脚本保存到一个单独的文件中,以便使用较少的技术能力(即不必提供将脚本放在相同路径中的说明)轻松共享和使用脚本。 – JohnLBevan

+2

我明白。不要分心你的实际问题,但你可以有一个主脚本调用你的远程脚本或在本地复制它们,但这确实增加了不必要的复杂程度。 – Matt

从反馈到目前为止,似乎在PowerShell v4中目前不可能实现我想要的功能。

我最终使用了一个简单的解决方法来实现这个目标。它增加了一点开销,但不是太痛苦。

  1. 从文件参数中删除ValidateScript验证。
  2. 创建一个新函数RUN,并精确复制文件的参数作为该函数的参数。在函数出现的脚本中,只要它在被调用之前就没有关系(参见步骤4)。
  3. 在此函数的定义中添加了ValidateScript段。
  4. 作为脚本的最后一行,这个脚本叫做RUN函数传递所有参数(为了简化/减少维护使用@PSBoundParameters)。
  5. 将可能已放置在主文件中的所有其他脚本逻辑(不包括函数定义)移至RUN函数的process块中。
  6. 当心疑难杂症:如果使用默认参数,您需要处理这些因为默认情况下,他们将不会被包含在第4步欲了解更多信息提到@PSBoundParameters,看到Parameters with default value not in PsBoundParameters?。如果您将默认逻辑与其他参数信息一起复制到函数定义中,则这不是问题。

#Run-DemoScript.ps1 
param (
    [Parameter(Mandatory = $true)] 
    [string]$DbInstance 
    , 
    [Parameter(Mandatory = $true)] 
    [string]$DbCatalog 
    , 
    [Parameter(Mandatory = $true)] 
    #[ValidateScript({Validate-Country -Country $_ -DbInstance $DbInstance -DbCatalog $DbCatalog})] 
    [string]$Country 
) 

#move all logic from main script into here 
#copy parameters from file's param definition, only add in validation 
function RUN { 
    param (
     [Parameter(Mandatory = $true)] 
     [string]$DbInstance 
     , 
     [Parameter(Mandatory = $true)] 
     [string]$DbCatalog 
     , 
     [Parameter(Mandatory = $true)] 
     [ValidateScript({Validate-Country -Country $_ -DbInstance $DbInstance -DbCatalog $DbCatalog})] 
     [string]$Country 
    ) 
    process { 
     "Script ran with Country $Country" 
    } 
} 

#returns $true if the country is in the database's country table; otherwise false 
function Validate-Country { 
    [CmdletBinding()] 
    param (
     [Parameter(Mandatory = $true)] 
     [string]$DbInstance 
     , 
     [Parameter(Mandatory = $true)] 
     [string]$DbCatalog 
     , 
     [Parameter(Mandatory = $true)] 
     [string]$Country 
    ) 
    process { 
     $Country = $Country -replace "'","''" 
     ((Execute-SQLQuery -DbInstance $DbInstance -DbCatalog $DbCatalog -Query "select top 1 1 x from dbo.Country where Name = '$Country'") | Measure-Object | Select -ExpandProperty Count) -gt 0 
    } 
} 

function Execute-SQLQuery { 
    #... 
} 

RUN @PSBoundParameters #remember to handle default parameters: https://stackoverflow.com/questions/2808973/parameters-with-default-value-not-in-psboundparameters 

如果你想保持一个脚本,你都可以在一个脚本中的功能。我的意思是这样的:

Function fun1 { 
     <code> 
     } 

    Function fun2 { 
     <code> 
     } 

    Function fun3 { 
     Param (
      Validatescript({ 
        fun1 
        })$Param1 
      ) 
     <code> 
     } 

    #start executing code 
    fun3 

这将使你从一个单一的文件运行脚本,也有你的功能除了脚本的验证。这很漂亮吗?不行吗?是。过去我已经做了好几次,并且工作得很好。您需要牢记的唯一事情就是您的脚本使用变量进行范围设定。

+2

在OP的脚本开始处使用'param'关键字时,无法执行此操作。 – Matt

+0

正确,它需要修改代码,但是它打破了OP想要什么?我刚刚看到,将它放在单个文件中对于使用脚本调用脚本比较容易。完全取决于OP,只是提供不同的观点。 –

+0

谢谢@POSHGeek;根据Matt的评论,我理想的是在文件本身的参数解决方案之后(尽管我没有在问题中明确提到这一点)。对于函数参数,函数的顺序无关紧要。 – JohnLBevan