【问题标题】:How to write out or trace specific commands in a PowerShell script?如何在 PowerShell 脚本中写出或跟踪特定命令?
【发布时间】:2018-08-21 03:45:18
【问题描述】:

在我正在创建的 PowerShell 脚本中,我想输出带有正在传递的参数值的特定命令。输出可以转到日志文件和/或控制台输出。以下将向控制台输出我想要的内容,但我必须复制感兴趣的脚本行,并且在某些时候会在命令不匹配的地方犯下一个微妙的错误。我试过Set-PSDebugTrace-Command 都没有给出我想要的结果。我曾想过将这行脚本放入一个字符串中,写出来,然后调用Invoke-Expression,但我会放弃自动完成/智能感知。

用于编写和执行的重复行示例:

Write-Output "New-AzureRmResourceGroup -Name $rgFullName -Location $location -Tag $tags -Force"
New-AzureRmResourceGroup -Name $rgFullName -Location $location -Tag $tags -Force

带有扩展变量的输出结果。 $tags 没有扩展到实际的哈希表值:

New-AzureRmResourceGroup -Name StorageAccounts -Location West US -Tag System.Collections.Hashtable -Force

我可以使用哪些其他选项或命令行开关来实现跟踪,而无需编写重复代码甚至扩展哈希表?

【问题讨论】:

    标签: powershell


    【解决方案1】:

    据我所知,没有内置功能可以与使用参数扩展中使用的变量和表达式执行的命令版本相呼应。

    即使有,它也只能在简单的情况下忠实工作,因为并非所有对象都有文字表示。

    但是,有限制,您可以基于&、调用operatorparameter splatting,通过预先定义的参数值哈希表推出自己的解决方案:

    # Sample argument values.
    $rgFullName = 'full name'
    $location = 'loc'
    $tags = @{ one = 1; two = 2; three = 3 }
    
    # Define the command to execute:
    #   * as a string variable that contains the command name / path
    #   * as a hashtable that defines the arguments to pass via
    #     splatting (see below.)
    $command = 'New-AzureRmResourceGroup'
    $commandArgs = [ordered] @{
      Name = $rgFullName
      Location = $location
      Tag = $tags
      Force = $True
    }
    
    # Echo the command to be executed.
    $command, $commandArgs
    
    # Execute the command, using & and splatting (note the '@' instead of '$')
    & $command @commandArgs
    

    以上内容与以下内容相呼应(不包括实际执行的任何输出):

    New-AzureRmResourceGroup
    
    Name                           Value
    ----                           -----
    Name                           full name
    Location                       loc
    Tag                            {two, three, one}
    Force                          True
    

    如你所见:

    • PowerShell 的默认输出格式会导致用于喷溅的哈希表的多行表示。

    • 不幸的是,$tags 条目本身是一个哈希表,仅由它的 键表示 - 缺少值。


    但是,您可以以编程方式自定义输出,以创建一个近似带有扩展参数的命令的单行表示,包括显示哈希表及其值,使用辅助函数convertTo-PseudoCommandLine

    # Helper function that converts a command name and its arguments specified
    # via a hashtable or array into a pseudo-command line string that 
    # *approximates* the command using literal values.
    # Main use is for logging, to reflect commands with their expanded arguments.
    function convertTo-PseudoCommandLine ($commandName, $commandArgs) {
    
      # Helper script block that transforms a single parameter-name/value pair
      # into part of a command line.
      $sbToCmdLineArg = { param($paramName, $arg) 
        $argTransformed = ''; $sep = ' '
        if ($arg -is [Collections.IDictionary]) { # hashtable
          $argTransformed = '@{{{0}}}' -f ($(foreach ($key in $arg.Keys) { '{0}={1}' -f (& $sbToCmdLineArg '' $key), (& $sbToCmdLineArg '' $arg[$key]) }) -join ';')
        } elseif ($arg -is [Collections.ICollection]) { # array / collection
          $argTransformed = $(foreach ($el in $arg) { & $sbToCmdLineArg $el }) -join ','
        }
        elseif ($arg -is [bool]) { # assume it is a switch
          $argTransformed = ('$False', '$True')[$arg]
          $sep = ':' # passing an argument to a switch requires -switch:<val> format
        } elseif ($arg -match '^[$@(]|\s|"') {
          $argTransformed = "'{0}'" -f ($arg -replace "'", "''") # single-quote and escape embedded single quotes
        } else {
          $argTransformed = "$arg" # stringify as is - no quoting needed
        }
        if ($paramName) { # a parameter-argument pair
          '-{0}{1}{2}' -f $paramName, $sep, $argTransformed
        } else { # the command name or a hashtable key or value
          $argTransformed
        }
      }
    
      # Synthesize and output the pseudo-command line.
      $cmdLine = (& $sbToCmdLineArg '' $commandName)
      if ($commandArgs -is [Collections.IDictionary]) { # hashtable
        $cmdLine += ' ' + 
          $(foreach ($param in $commandArgs.Keys) { & $sbToCmdLineArg $param $commandArgs[$param] }) -join ' '
      } elseif ($commandArgs) { # array / other collection
        $cmdLine += ' ' + 
          $(foreach ($arg in $commandArgs) { & $sbToCmdLineArg '' $arg }) -join ' '
      }
    
      # Output the command line.
      # If the comamnd name ended up quoted, we must prepend '& '
      if ($cmdLine[0] -eq "'") {
        "& $cmdLine"
      } else {
        $cmdLine
      }
    
    }
    

    定义了convertTo-PseudoCommandLine之前或以上下面的代码),然后您可以使用:

    # Sample argument values.
    $rgFullName = 'full name'
    $location = 'loc'
    $tags = @{ one = 1; two = 2; three = 3 }
    
    # Define the command to execute:
    #   * as a string variable that contains the command name / path
    #   * as a hashtable that defines the arguments to pass via
    #     splatting (see below.)
    $command = 'New-AzureRmResourceGroup'
    $commandArgs = [ordered] @{
      Name = $rgFullName
      Location = $location
      Tag = $tags
      Force = $True
    }
    
    
    # Echo the command to be executed as a pseud-command line
    # created by the helper function.
    convertTo-PseudoCommandLine $command $commandArgs
    
    # Execute the command, using & and splatting (note the '@' instead of '$')
    & $command @commandArgs
    

    这会产生(不包括实际执行的任何输出):

    New-AzureRmResourceGroup -Name 'full name' -Location loc -Tag @{two=2;three=3;one=1} -Force:$True
    

    【讨论】:

      猜你喜欢
      • 2011-10-29
      • 2019-04-24
      • 2022-07-15
      • 1970-01-01
      • 2019-10-26
      • 1970-01-01
      • 2019-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多