【问题标题】:How to run a PowerShell script with verbose output?如何运行具有详细输出的 PowerShell 脚本?
【发布时间】:2021-08-15 18:21:07
【问题描述】:

我想知道是否有一种方法可以运行 PowerShell 脚本,以便打印脚本每一行的命令和输出。例如,在 Bash 中,您可以编写 bash -x myscript 或在脚本顶部放置 set -x。在 Batch 中,您将省略传统上留在脚本顶部的 @echo off。 PowerShell 是否具有与这些结构等效的功能?

我尝试过的事情:运行powershell -? | sls verbose,但没有任何结果。

【问题讨论】:

    标签: windows powershell shell windows-10 trace


    【解决方案1】:

    注意:此答案最初是针对 Windows Powershell needs to print out information for a particular command regardless of whether it successfully executed or Not 的重复问题发布的。

    补充和详述Michael Sorens' helpful answer

    有限制,你可以使用Set-PSDebug -Trace 1让PowerShell在脚本语句执行前为你回显;然而,这更像bashset -o verbose / set -v 选项而不是set -o xtrace / set -x,因为回显的命令是未扩展的;详情如下:

    # Start tracing statements.
    Set-PSDebug -Trace 1
    
    try 
    {
    
      # Sample command
      cmd /c echo 'hi there' $HOME
    
    }
    finally {
      Set-PSDebug -Trace 0 # Turn tracing back off.
    }
    

    以上产出:

    DEBUG:    4+  >>>> cmd /c echo 'hi there' $HOME
    "hi there" C:\Users\jdoe
    DEBUG:    6+  >>>> Set-PSDebug -Trace 0 # Turn tracing off.
    

    虽然这种方法几乎不需要额外的努力,但它的局限性是:

    • 您无法控制跟踪语句的前缀。 (例如DEBUG: 4+ >>>> ,其中4 是行号。

    • 关闭回溯关闭总是会产生一个追踪语句。

    • 没有办法捕获或抑制跟踪输出 - 它总是打印到主机(控制台);但是,您可以通过 PowerShell 的 CLI 从 外部 PowerShell 捕获它 - 请参阅 this answer

    • 从 PowerShell 7.2 开始,使用续行符跨越多行的命令只会回显它们的 第一 行(请参阅 GitHub issue #8113)。

    • 也许最重要的是,回显的语句是它们的字面源代码表示,因此可以包含未扩展的变量引用和表达式,例如上例中的$HOME

      • GitHub issue #9463 建议扩展 值(即,变量和表达式用它们的值替换),例如在bash 中使用set -x
      • 虽然这对于调用 外部程序 是可行的 - 无论如何,其参数总是 strings,但挑战在于调用 PowerShell 命令 支持任意 .NET 类型的参数,并非所有的参数都具有忠实的字符串文字表示;也就是说,即使是仅用于人类眼球的字符串表示也可以说比 未扩展 值更可取。

    如果您需要查看扩展参数值和/或控制输出格式/目标

    注意:

    • 虽然 Invoke-Expression (iex) should generally be avoided,但由于其固有的安全风险以及通常有更好、更安全的选择,它确实提供了一个解决方案 - 与往常一样,请确保您完全控制传递给Invoke-Expression的字符串中的内容,以避免潜在的不需要的命令注入。

    • 解决方案要求您构造字符串以传递给Invoke-Expression,并使用预先扩展(字符串插值),以便生成的命令行在执行时仅包含文字参数,以便回显命令行可以全面了解调用的可执行文件及其参数是什么。

      • 如上所述,这只有在您调用外部程序(例如msbuild)时才可能实现。

    首先,定义一个辅助函数,它接受一个命令行字符串,回显它,然后通过Invoke-Expression 执行它:

    # !! IMPORTANT:
    # !! Only pass *trusted* strings to this function - the
    # !! argument is blindly passed to Invoke-Expression.
    function Invoke-AfterEchoing {
      param([string] $commandLine)
    
      # Echo the command line to be executed,
      # using the verbose output stream in this example:
      Write-Verbose -Verbose "Executing: $commandLine"
    
      Invoke-Expression $commandLine
    
    }
    

    现在您可以构建命令行字符串并将它们传递给辅助函数:

    Invoke-AfterEchoing @"
    & "$msbuild" .\blabBlah.csproj /t:Clean
    "@
    
    
    Invoke-AfterEchoing @"
    & "$msbuild" .\blabBlah.csproj /t:Build /p:Configuration=Dev
    "@
    

    注意:

    • 可扩展的here-string (@"<newline>...<newline>"@) 用于简化字符串内部引用。

      • 选择 expandable 形式是为了确保可执行路径和所有参数在前面展开,因此作为它们的文字值嵌入到结果字符串,以便回显字符串将显示所有实际值。
    • &call operator,用于调用msbuild,这在语法上是必要的,因为它的路径被传递引用,如果$msbuild包含带空格的路径

    输出将如下所示:

    VERBOSE: Executing: & "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" .\blabBlah.csproj /t:Clean
    # ... (output)
    
    VERBOSE: Executing: & "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" .\blabBlah.csproj /t:Build /p:Configuration=Dev
    # ... (output)
    

    【讨论】:

      【解决方案2】:

      要让PowerShell脚本能够从参数/命令行接收参数,即使没有任何参数,也需要添加[CmdletBinding()] param ()

      示例脚本:Test-Output.ps1

      [CmdletBinding()] param ()
      Write-Host "Test output on OS $($Env:OS)"
      Write-Verbose "Test VERBOSE output on OS $($Env:OS)"
      
      1. 在 PowerShell 中执行脚本:
      PS C:\> .\Test-Output.ps1 -Verbose
      
      1. 在 Linux 上的 PowerShell 中执行脚本:
      /$ pwsh
      PS /> ./Test-Output.ps1 -Verbose
      
      1. 在 Windows 上使用 PowerShell.exe 执行脚本:
      C:\> powershell.exe Test-Output.ps1 -Verbose
      
      1. 在 Windows 上使用 pwsh.exe PowerShell Core 执行脚本:
      C:\> pwsh.exe Test-Output.ps1 -Verbose
      
      1. 在 Linux 上使用 pwsh PowerShell Core 执行脚本:
      /$ pwsh Test-Output.ps1 -Verbose
      

      Windows 上的示例输出:

      Test output on OS Windows_NT
      VERBOSE: Test VERBOSE output on OS Windows_NT
      

      Linux 上的示例输出:

      Test output on OS 
      VERBOSE: Test VERBOSE output on OS 
      

      【讨论】:

        【解决方案3】:

        如果您在脚本中使用 write-verbose,这将自动发生,

        但是,如果您需要手动编写函数的详细输出,则需要手动检查是否使用详细标志调用每个函数。这可以通过checking$PSCmdlet.MyInvocation.BoundParameters["Verbose"] 完成 从你的函数内部。

        【讨论】:

          【解决方案4】:

          这实际上非常简单,每个 PowerShell CMDLET 都有一个内置的 Verbose 标记。例如,您所要做的就是:

          Test-Connection -ComputerName www.google.com -Verbose
          

          就是这样。我希望这会有所帮助

          【讨论】:

          • 是的,谢谢,这正是我想要的!对于像我这样的人来说,历史、理论和 Bash 完全无关紧要。
          【解决方案5】:

          只是表明,@JamesKo,如果你问错了问题,你会得到错误的答案 :-(。有几个人基于 (a) 缺乏 Linux 暴露和 (b) 你的使用提出了善意的答案。 verbose 一词。在下文中,我将向您介绍 Linux 在此主题上与 PowerShell 的关系,但如果您赶时间,请随时跳到最后的答案。:-)

          背景

          在 PowerShell 中,verbose 有一个非常具体的含义,PowerShell man page 甚至相当模糊:

          显示有关执行的操作的详细信息 命令。此信息类似于跟踪中的信息或 事务日志。该参数仅在命令生成时有效 一条详细的消息。

          它甚至听起来像你想要的......但让我们将它与set -x 的 Linux 文档进行比较,根据你的 Linux 风格,可能是这样的(来自man-pages project)......

          shell 将在之后的每个命令的跟踪中写入标准错误 它会在执行命令之前扩展命令。

          或者这个(来自gnu)...

          打印简单命令的痕迹,对于命令,case命令,select 命令,以及命令及其参数的算术或 相关单词列表在它们展开之后和之前 执行。

          您问题的第一行清楚而简洁地同意这些。但是 PowerShell 中的冗长是不同的。简而言之,打开详细模式(无论是使用-Verbose 命令行开关还是$VerbosePreference 变量)只是启用从详细流到控制台的输出。 (就像 Linux 提供两个流,stdout 和 stderr 一样,PowerShell 提供多个流:输出流、错误流、警告流、详细流和调试流。您使用这些流的方式与 Linux 相同——您可以例如,甚至可以使用commands 4>&1 将详细流合并到标准输出。(您可以在PowerShell One-Liners: Accessing, Handling and Writing Data 的基本写入流部分阅读更多关于PowerShell 的多个输出流的信息,Complete Guide to PowerShell Punctuation 是一个很好的快速参考。 )

          答案

          Set-PSDebug 命令将为您提供与 bash 等效的跟踪。您甚至可以使用-Trace 参数调整跟踪细节。首先,这里是控件,在使用Set-PSDebug之前:

          PS> Get-PSDepth
          0
          

          值为 1 时,您会在执行时获得每一行代码,例如:

          PS> Set-PSDebug -Trace 1
          PS> Get-PSDepth
          DEBUG:    1+  >>>> Get-PSDepth
          DEBUG:  141+  >>>> {
          DEBUG:  142+   >>>> $nest = -1
          DEBUG:  143+   >>>> $thisId = $pid
          DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
          DEBUG:  145+    >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
          DEBUG:  146+    >>>> $nest++
          DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
          DEBUG:  148+   >>>> $nest
          0
          DEBUG:  149+  >>>> }
          

          使用 2 的值,您还可以获得变量赋值和代码路径:

          PS> Set-PSDebug -Trace 2
          PS> Get-PSDepth
          DEBUG:    1+  >>>> Get-PSDepth
          DEBUG:     ! CALL function '<ScriptBlock>'
          DEBUG:  141+  >>>> {
          DEBUG:     ! CALL function 'Get-PSDepth'  (defined in file 'C:\Users\msorens\Documents\WindowsPowerShell\profile.ps1')
          DEBUG:  142+   >>>> $nest = -1
          DEBUG:     ! SET $nest = '-1'.
          DEBUG:  143+   >>>> $thisId = $pid
          DEBUG:     ! SET $thisId = '9872'.
          DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
          DEBUG:  145+    >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
          DEBUG:     ! SET $thisId = '10548'.
          DEBUG:  146+    >>>> $nest++
          DEBUG:     ! SET $nest = '0'.
          DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
          DEBUG:  148+   >>>> $nest
          0
          DEBUG:  149+  >>>> }
          

          这些是我编写的名为 Get-PSDepth 的简单 cmdlet 的痕迹。它打印带有 DEBUG 前缀的命令、分配等,并与实际输出混合在一起,在这种情况下,它是仅包含 0 的单行。

          【讨论】:

            【解决方案6】:

            您始终可以在脚本中使用以下内容。

            $VerbosePreference="继续"

            注意:您必须以提升模式打开外壳。

            以下截图仅供参考。

            希望对你有帮助。

            【讨论】:

            • 尽管 OP 使用了 verbose 一词,但他们真正的意思是 执行跟踪。但是,最好了解preference variable $VerbosePreference 变量以打开详细输出内置于目标命令,但请注意不需要设置它。此外,尚不清楚您的屏幕截图中的Get-Date 是如何产生显示的详细输出的。
            猜你喜欢
            • 2018-08-13
            • 2014-03-23
            • 1970-01-01
            • 2011-08-22
            • 2019-06-21
            • 1970-01-01
            • 2017-12-24
            • 1970-01-01
            相关资源
            最近更新 更多