【问题标题】:In PowerShell can you Write-Host the invocation and arguments for an external command?在 PowerShell 中,您可以写入托管外部命令的调用和参数吗?
【发布时间】:2021-03-10 19:35:56
【问题描述】:

在我的 PowerShell (v5) 中,我正在调用许多外部命令,例如 msbuild、dotnet 等(我正在制作 PowerShell 构建脚本)。

我想记录正在运行的命令。我该怎么做?

这是一个示例,但我不想将命令复制为 Write-Host 语句。

Write-Host "dotnet restore --source $MyNuGet --source $NuGet"
dotnet restore --source $MyNuGet --source $NuGet

我希望我的日志中的输出是:

dotnet restore --source https://blah --source https://blah2

有没有办法做到这一点?我可以创建某种命令执行包装器吗?或者我可以将命令构建为字符串,然后我可以将其传递给 Write-Host 并将其传递给 Run-Command 或类似的东西?也许我会回答我自己的问题。我想知道我是否可以将字符串传递给“&”。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    我承认,一种更暴力的方法是:

    Start-Transcript <path to output file>
    ... run your commands ...
    Stop-Transcript
    

    并且您将有一个非常详细的日志来梳理(特别是如果您以交互方式运行并且有自定义提示代码)。请注意,这是通过注册表项启用的,但在 Windows 10 中,它似乎是默认启用的。

    或者,更符合您的原始示例:

    function Invoke-Wrapper {
        param(
            [Parameter(Mandatory=$true)][scriptblock]$ScriptBlock
        )
        Write-Host "$ScriptBlock"
        
        Invoke-Command -ScriptBlock $ScriptBlock
        $rc = Invoke-Command -ScriptBlock { $lastExitCode } 
    
        Write-Host "{ $ScriptBlock } returned: $rc"
        if ($rc -ne 0){
            exit $rc
        }
    }
    
    Invoke-Wrapper { my command(s) }
    
    

    【讨论】:

    • 嗨Guac!非常感激。一个问题是 Invoke-Command 吃掉了所有的命令输出。我试着做$stdout = Invoke-Command,但所有的换行符都不见了。我放弃了。
    • 你没有做双重包装脚本块的事情,是吗,就像$sb = { do something that creates output}; Invoke-Command -ScriptBlock { $sb }一样?
    • 不,我不是。对不起
    【解决方案2】:

    当我完成输入问题时,我想到了一个包装器的想法并找到了Invoke-Expression。所以我能够像这样创建一个很酷的包装器:

    function Invoke-Wrapper {
        param(
            [Parameter(Mandatory=$true)][string]$Expression
        )
        Write-Host "$Expression"
        Invoke-Expression $Expression
        Write-Host $Expression.Substring(0, 12)"... exit code: $lastExitCode"
        if ($LastExitCode -ne 0){
            exit $LastExitCode
        }
    }
    

    我可以这样称呼它:

    Invoke-Wrapper "dotnet restore --source $CustomNuGet --source $NuGet"
    
    Invoke-Wrapper "dotnet publish $StartUpProjectFilePath --configuration Release --version-suffix $AppVersion"
    
    Invoke-Wrapper "Compress-Archive -Force $StartUpProjectDriectory\bin\Release\netcoreapp*\publish\*  $pwd\Artifact.$StartUpProjectName.$AppVersion.$buildResultKey.zip"
    

    我能想到的唯一问题是,如果命令带有引号,它们可能会搞砸,但我的第三个示例有效。还有其他答案或更简单的想法吗?

    【讨论】:

    • 小心...您可能会因为在 Powershell 答案中使用 Invoke-Expression 而被否决...有点像在 Javascript 中使用 eval...
    • 哈哈。只要我学就好了。如果它不好,我总是可以删除这个答案。你有别的想法吗?
    • 试试我的最后一个例子。它将您的字符串交换为脚本块,这可能会解决您的引用问题,并避免Invoke-Expression
    • 这个答案似乎可以正确显示标准输出,但我放弃了包装器的想法。
    【解决方案3】:

    这是一个不同的想法,大部分完成了我想要的。它不那么迷人,但我需要妥协,因为其他想法开始陷入困境。如果我在 PowerShell 方面做得更好,我也许可以让它们工作。

    相反,我只是在命令前后调用 Log-* 函数。它不会记录完整的命令,但我可以通过查看脚本来弄清楚。

    function Log-Begin {
        param(
            [Parameter(Mandatory=$true)][string]$CommandName
        )
        Write-Host " "
        Write-Host "Calling: $CommandName"
    }
    
    function Log-End {
        param(
            [Parameter(Mandatory=$true)][string]$CommandName,
            [Parameter(Mandatory=$true)][int]$exitCode
        )
        Write-Host "Finished: $CommandName"
        Write-Host "        : returned exit code: $exitCode."
        if ($exitCode -ne 0){
            Write-Host "Got a bad exitCode: $exitCode.  Exiting $PSCommandPath."
            exit $exitCode
        }
    }
    

    用法:

    Log-Begin "dotnet restore"
    dotnet restore --source $CustomNuGet --source $NuGet
    Log-End "dotnet restore" $LastExitCode
    

    现在至少我所有的命令都有通用的日志记录和返回码处理。

    【讨论】:

      猜你喜欢
      • 2020-12-28
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      • 2013-06-01
      • 2019-07-11
      • 2018-09-22
      • 1970-01-01
      相关资源
      最近更新 更多