为什么将脚本块包装到 commandlet 函数中会吞噬所有输出?

Why does wrapping a script block into a commandlet function swallow all output?

提问人:Dbl 提问时间:11/13/2023 最后编辑:Dbl 更新时间:11/13/2023 访问量:41

问:

下面的脚本在直接从 .ps1 文件执行时工作得很好,但自从我把它放在 .psm1 文件中后,该函数仍然有效,但不再将输出重定向到输出。我正在寻找一种方法来让它也从函数中打印其输出。将其移回 ps1 不是一种选择。&dotnet ...

我尝试了

尝试

& dotnet *>&1
& dotnet | Out-Host
& dotnet | Write-Host

但显然我的 PowerShell 知识在这里太有限了。

函数.psm1:


function Build-Android {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline)]
        [string] $KeyStorePath, 

        [Parameter(Mandatory,ValueFromPipeline)]
        [string] $SignPassword, 

        [Parameter(Mandatory,ValueFromPipeline)]
        [string] $ProjectPath, 

        [Parameter(Mandatory,ValueFromPipeline)]
        [string] $PublishDirectory
    )

    [Environment]::SetEnvironmentVariable('PCR3PW', $SignPassword, 'Process')

    $keyAlias = "PCR3"
    $keyPass = "env:PCR3PW"
    $storePass = "env:PCR3PW"

    # https://learn.microsoft.com/de-de/dotnet/maui/android/deployment/publish-cli 
    $publishCode = "dotnet publish $ProjectPath -f net7.0-android -c Release -o `"$PublishDirectory`" -p:AndroidKeyStore=true -p:AndroidSigningKeyStore=$KeyStorePath -p:AndroidSigningKeyAlias=$keyAlias -p:AndroidSigningKeyPass=$keyPass -p:AndroidSigningStorePass=$storePass"
    Write-Host "$publishCode" -ForegroundColor DarkGreen

    #Invoke-Expression -Command $publishCode -ErrorAction Stop
    &dotnet publish $ProjectPath -f net7.0-android -c Release -o `"$PublishDirectory`" -p:AndroidKeyStore=true -p:AndroidSigningKeyStore=$KeyStorePath -p:AndroidSigningKeyAlias=$keyAlias -p:AndroidSigningKeyPass=$keyPass -p:AndroidSigningStorePass=$storePass
    
    if($LASTEXITCODE -ne 0)    
        throw "There was an issue running the specified dotnet command."

    $apk = Get-ChildItem "$PublishDst" -Filter "*.apk" | %{$_.FullName}

    return $apk
}

MyScript.ps1:

param (
    [Parameter(Mandatory=$true, HelpMessage="Signing password for publish process")]
    [string]$SignPassword,
    [Parameter(Mandatory=$true, HelpMessage="Destination folder for APK publish")]
    [string]$PublishDst
)

Import-Module ".\functions.psm1"

$keyStore = [System.Uri]::new([System.Uri]::new("$PSScriptRoot\.", [System.UriKind]::Absolute), [System.Uri]::new("PCR3.keystore", [System.UriKind]::Relative)).LocalPath
$apkProject = [System.Uri]::new([System.Uri]::new("$PSScriptRoot\.", [System.UriKind]::Absolute), [System.Uri]::new("..\src\MyProject\MyProject.csproj", [System.UriKind]::Relative)).LocalPath

$apkPath = Build-Android -KeyStorePath $keyStore -SignPassword $SignPassword -ProjectPath $apkProject -PublishDirectory $PublishDst

已检查的参考资料

我已经对此进行了研究,以寻找有关如何重定向输出的线索: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.3

根本问题

这在某种程度上显然是终端的原因。虽然 $PSVersionTable 与 PS Core 7 相同,但 Terminal 不会从 & 表达式生成输出,而 powershell 本身会生成输出。

PowerShell Windows 终端 PowerShell-7

评论

0赞 Mathias R. Jessen 11/13/2023
您如何导入和调用该函数?FWIW 您的函数中存在语法错误,之后缺少Build-Android{if($LASTEXITCODE -ne 0)
0赞 Dbl 11/13/2023
@MathiasR.Jessen 添加了调用代码。{ 我猜是 PS7 的可选。 这段代码对我来说运行没有问题
0赞 Mathias R. Jessen 11/13/2023
啊,你必须运行的是 7.4-preview
0赞 Dbl 11/13/2023
7.2.16 实际上

答:

3赞 Mathias R. Jessen 11/13/2023 #1

查看通话站点:

$apkPath = Build-Android -KeyStorePath $keyStore -SignPassword $SignPassword -ProjectPath $apkProject -PublishDirectory $PublishDst

您正在将输出分配给局部变量,因此输出将结束。Build-Android$apkPath

如果要回避正常的输出流,而是立即将其打印到主机应用程序,请使用:Out-Host

&dotnet publish $ProjectPath -f net7.0-android -c Release -o `"$PublishDirectory`" -p:AndroidKeyStore=true -p:AndroidSigningKeyStore=$KeyStorePath -p:AndroidSigningKeyAlias=$keyAlias -p:AndroidSigningKeyPass=$keyPass -p:AndroidSigningStorePass=$storePass |Out-Host

下面是一个简化的示例,显示了 instead 的效果:ping.exe

PS ~> @'
>> function f {
>>   [CmdletBinding()]
>>   param()
>>
>>   ping.exe 1.1.1.1 -n 1 |Out-Host
>>
>>   return 'Not from ping'
>> }
>> '@ |Set-Content myModule.psm1
PS ~> Import-Module myModule.psm1 -Force
PS ~> $functionOutput = f

Pinging 1.1.1.1 with 32 bytes of data:
Reply from 1.1.1.1: bytes=32 time=13ms TTL=56

Ping statistics for 1.1.1.1:
    Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 13ms, Maximum = 13ms, Average = 13ms
PS ~> $functionOutput
Not from ping

如您所见,通过管道传递到的输出在执行时会立即直接写入主机应用程序的屏幕缓冲区,而未写入的常规输出最终会写入变量。Out-Hostf

评论

0赞 Dbl 11/13/2023
这只是返回值实际落地的地方。我尝试了Out-Host,但没有成功。你验证了你的答案吗?如果您删除我的代码并执行 &ping 或其他操作,这可能也应该有效
0赞 Mathias R. Jessen 11/13/2023
@Dbl 您似乎误解了 PowerShell 中关键字的功能 - 任何未以其他方式重定向或分配的输出都将“冒泡”给调用方,无论它是否是语句的一部分returnreturn
0赞 Dbl 11/13/2023
我知道这一点。我的问题不是函数的返回值。我需要&dotnet的输出...登陆主机输出。如果我剥离所有代码并只使用 &ping,我可以减少问题。一旦我进入功能块,我的命令就不再出现在主机中。
0赞 Mathias R. Jessen 11/13/2023
@Dbl 即使你用管道管到?Out-Host
0赞 Dbl 11/13/2023
奇怪的是 - 是的。我记得一堆powershell脚本,每当我想捕获&表达式的输出时,我几乎都这样做了。但是出于某种原因,每当我从 cmdlet 执行此操作时,它都不起作用。好像我缺少什么属性之类的