为什么此PowerShell函数的默认参数会根据使用情况更改值.或&以在其中调用命令?

Chr*_*ood 13 powershell

我遇到了一些奇怪的行为,其默认参数似乎null根据该函数是否使用了一个.&在其中调用本机命令来更改值(或空字符串).

这是一个带有两个相同函数的示例脚本,唯一的区别是它们如何调用本机命令(cmd.exe):

function Format-Type($value)
{
    if ($value -eq $null) { '(null)' } else { $value.GetType().FullName }
}

function Use-Dot
{
    param(
        [string] $Arg = [System.Management.Automation.Language.NullString]::Value
    )

    Write-Host ".: $(Format-Type $Arg)"

    . cmd.exe /c exit 0
}

function Use-Ampersand
{
    param(
        [string] $Arg = [System.Management.Automation.Language.NullString]::Value
    )

    Write-Host "&: $(Format-Type $Arg)"

    & cmd.exe /c exit 0
}

Use-Dot
Use-Ampersand
Run Code Online (Sandbox Code Playgroud)

在PowerShell 5.1上,我得到以下输出,显示参数的值在两种情况下是不同的:

.: (null)
&: System.String
Run Code Online (Sandbox Code Playgroud)

以这种方式关联这种行为听起来很荒谬,因此我确信我必须在这里遗漏一些明显(或者可能非常微妙)的东西,但那又是什么呢?

-

问题PowerShell管道中.简写是什么?讨论了.和之间范围的差异,&但没有提到为什么在命令调用中甚至没有引用的默认参数可能会受到其使用的影响.在我的示例中,调用程序调用命令之前似乎受到影响.

She*_*onH 1

花了一些时间对此进行深入研究,这就是我所观察到的。

首先为了清楚起见,我不认为您应该在基本比较中将 NullString 值视为与 null 相同。也不知道为什么你需要这个,因为这通常是我期望从 C# 开发中得到的。您应该能够使用$nullPowerShell 来完成大多数工作。



if($null -eq [System.Management.Automation.Language.NullString]::Value)
{
    write-host "`$null -eq [System.Management.Automation.Language.NullString]::Value"
}
else
{
    write-host "`$null -ne [System.Management.Automation.Language.NullString]::Value"
}
Run Code Online (Sandbox Code Playgroud)

其次,问题不一定是由于呼叫运营商,即&。我相信您正在处理底层参数绑定强制。强数据类型绝对是 PowerShell 的一个薄弱环节,因为即使显式声明也[int]$val可能最终被 PowerShell 在写入 时在下一行中自动设置为字符串类型Write-Host $Val

为了识别底层行为,我使用了Trace-Command函数(Trace Command)。

我将 Use-Dot 更改为仅调用该函数,因为不需要写入主机来输出字符串。


function Use-Ampersand
{
    param(
        [string]$NullString = [System.Management.Automation.Language.NullString]::Value
    )
    Format-Type $NullString
    &cmd.exe /c exit 0
}

Run Code Online (Sandbox Code Playgroud)

我修改的格式类型也使用了$null左侧被认为是更好的做法,这也是由于类型推断。

function Format-Type($v= [System.Management.Automation.Language.NullString]::Value)
{

    if ($null  -eq $v)
    {       
     '(null)'
    }
    else {
        $v.GetType().FullName
     }
}
Run Code Online (Sandbox Code Playgroud)

为了缩小数据类型的问题范围,我使用了以下命令,尽管这不是我深入了解该问题的地方。直接调用时两者的工作原理相同。

Trace-Command -Name TypeConversion -Expression { Format-Type $NullString} -PSHost
Trace-Command -Name TypeConversion -Expression { Format-Type ([System.Management.Automation.Language.NullString]$NullString) } -PSHost
Run Code Online (Sandbox Code Playgroud)

但是,当我使用 TypeConversion 跟踪运行函数时,它显示了转换的差异,这可能解释了您观察到的一些行为。

Trace-Command -Name TypeConversion  -Expression { Use-Dot} -PSHost
Trace-Command -Name TypeConversion  -Expression { Use-Ampersand} -PSHost
Run Code Online (Sandbox Code Playgroud)
# USE DOT
DEBUG: TypeConversion Information: 0 :  Converting "" to "System.String".
DEBUG: TypeConversion Information: 0 :      Converting object to string.
DEBUG: TypeConversion Information: 0 :  Converting "" to "System.Object". <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 :  Converting ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL" to "System.String".
DEBUG: TypeConversion Information: 0 :      Result type is assignable from value to convert's type
Run Code Online (Sandbox Code Playgroud)

OUTPUT: (null)

# Use-Ampersand
DEBUG: TypeConversion Information: 0 : Converting "" to "System.String".
DEBUG: TypeConversion Information: 0 :     Converting object to string.
DEBUG: TypeConversion Information: 0 : Converting "" to "System.String". <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 :     Converting null to "".        <<<<<<<<<<<
DEBUG: TypeConversion Information: 0 : Converting ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL" to "System.String".
DEBUG: TypeConversion Information: 0 :     Result type is assignable from value to convert's type

Run Code Online (Sandbox Code Playgroud)

OUTPUT: System.String

显着的区别在于它显示了vsUse-Ampersand的声明。在 PowerShell 中,. 空字符串比较将通过空检查,导致输出成功。Converting null to ""Converting "" to "System.Object"$null <> [string]''GetType()

关于 PowerShell 方法的一些想法

为什么要这样做,我不确定,但在你投入更多时间研究之前,让我基于艰苦的学习提供一条建议。

如果开始处理由于尝试在 PowerShell 中强制数据类型而导致的问题,请首先考虑 PowerShell 是否是适合该工作的工具

是的,您可以使用类型扩展。是的,您可以使用 .NET 数据类型,$List = [System.Collections.Generic.List[string]]::new()并且可以强制执行一些 .NET 类型规则。然而,PowerShell 并不是像 C# 那样被设计为强类型语言。尝试这样处理会导致许多困难。虽然我是 PowerShell 的忠实粉丝,但我已经认识到应该欣赏它的灵活性,并尊重它的限制。

如果我确实遇到了如此强烈需要映射的问题[System.Management.Automation.Language.NullString]::Value,我会考虑我的方法。

也就是说,这是一项具有挑战性的调查,我不得不尝试一下,同时在事后提供我的 10 美分。

其他资源

在发布我的答案后,我发现了另一个似乎相关的答案,并且也支持了不正常使用的说法[NullString],因为它在 PowerShell 中的使用并不是真正的设计目的。