如何在PowerShell脚本中写出或跟踪特定命令?

时间:2018-08-19 23:27:20

标签: powershell

在我正在创建的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

在不编写重复代码甚至扩展哈希表的情况下,我还可以使用其他哪些选项或Commandlet实现跟踪?

1 个答案:

答案 0 :(得分:1)

我不知道有内置功能会回显正在使用参数 expanded 中使用的变量和表达式执行的命令版本。

即使有,它也只能在简单的情况下忠实地运行,因为并非所有对象都有 literal 表示形式。

但是,有一定的局限性,您可以通过预先定义的参数值哈希表基于&,调用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项本身就是一个哈希表,仅由其表示-值缺失。


但是,您可以以编程方式自定义输出 ,以创建单行表示形式,以扩展的参数近似命令< / strong>,包括使用 helper函数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