为什么一个 CmdLet 的文本输出是格式化的,而另一个 CmdLet 的文本输出不是?

Why is text output from one CmdLet formatted and text output from another is not?

我写了 2 个 cmdlet,用于在 GUID 和 Oracle 的等效 Guid 之间转换,它为用于存储 GUID 的 RAW 列显示的字符串。第一个,Convert-GuidToRaw,接受一个 GUID 字符串参数并输出一个原始字符串:

var raw = GuidConverter.Core.GuidConverter.ToRaw(Input);
WriteObject(raw);

其中 raw 的类型为 string。当我 运行 这个 cmdlet 时,我得到一个简单明了的字符串输出:

PS D:\SANRAL\NRA2> New-Guid | Convert-GuidToRaw                                                                  
DD8386EE09231A43B7731880CCAD6B87
PS D:\SANRAL\NRA2>

我的其他 cmdlet Convert-RawToGuid 接受表示 GUID 的 RAW 字符串参数并输出原始 GUID:

var guid = GuidConverter.Core.GuidConverter.FromRaw(Input);
WriteObject(guid);

其中 guid 的类型为 Guid。当我 运行 这个 cmdlet 时,我得到的输出格式类似于 table:

PS D:\SANRAL\NRA2> Convert-RawToGuid DD8386EE09231A43B7731880CCAD6B87

Guid
----
ee8683dd-2309-431a-b773-1880ccad6b87


PS D:\SANRAL\NRA2>

在这两种情况下,我都输出单个对象,而不是列表,并且在这两种情况下,对象都可以很容易地用简单的字符串表示。当我 return 一个 Guid 类型时,为什么我得到表格输出?

可能值得注意的是,Convert-GuidToRaw cmdlet 及其普通、未格式化的字符串输出似乎无法正确写入管道。两个 cmdlet 都从管道中获取一个参数。我希望下面的这个 cmdlet 管道输出一个 GUID,而管道中的最后一个 cmdlet 直到寻找输入:

PS C:\WINDOWS\system32> New-Guid | Convert-GuidToRaw | Convert-RawToGuid

cmdlet Convert-RawToGuid at command pipeline position 3
Supply values for the following parameters:
Input:

为什么 Convert-RawToGuid 没有从 Convert-GuidToRaw 得到 "pipelined" 字符串?如下交换管道的顺序,会导致预期的行为:

PS C:\WINDOWS\system32> Convert-RawToGuid F3BD9411DE8E4F4BBCACECFCED6D305D | Convert-GuidToRaw
F3BD9411DE8E4F4BBCACECFCED6D305D
PS C:\WINDOWS\system32>

Convert-RawToGuid cmdlet 如下所示:

[Cmdlet(VerbsData.Convert, "RawToGuid")]
public class ConvertRawToGuidCommand : System.Management.Automation.Cmdlet
{
    [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
    public string Input { get; set; }

    protected override void ProcessRecord()
    {
        if (string.IsNullOrEmpty(Input) || Input.Length != 32)
        {
            throw new ArgumentException("Input must be a 32 character hex string");
        }
        var guid = GuidConverter.Core.GuidConverter.FromRaw(Input);
        WriteObject(guid);
    }
}

Convert-GuidToRaw cmdlet 如下所示:

[Cmdlet(VerbsData.Convert, "GuidToRaw")]
public class ConvertGuidToRawCommand : System.Management.Automation.Cmdlet
{
    [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
    public Guid Input { get; set; }

    protected override void ProcessRecord()
    {
        var raw = GuidConverter.Core.GuidConverter.ToRaw(Input);
        WriteObject(raw);
    }
}

第 1 部分

In both cases I output a single object, not a list, and in both cases the object is easily represented by a simple string. Why do I get tabular output when I return a Guid type?

当交互式会话中的命令 returns 未分配值时,PowerShell 将其传递给默认值 Format Command to generate the text before it's written to the console. PowerShell is pre-configured with a bunch of Format Commands for some specific types, and System.Guid 是其中一种类型。

例如:

PS> [Guid]::NewGuid()

Guid
----
ee737b6f-7f68-4015-8841-1278b37a6420

各种类型的默认格式化程序在 PowerShell 5.1 的 *.Format.ps1xml 文件中配置,并从 PowerShell 6 开始按照 About Format.ps1xml

嵌入到源代码中

在 PowerShell 5.1 中,System.Guid 的默认格式化程序配置可以在 $PSHOME\DotNetTypes.format.ps1xml 中找到,如下所示:

<View>
  <Name>System.Guid</Name>
  <ViewSelectedBy>
    <TypeName>System.Guid</TypeName>
  </ViewSelectedBy>
  <TableControl>
    <TableRowEntries>
      <TableRowEntry>
        <TableColumnItems>
          <TableColumnItem>
            <PropertyName>Guid</PropertyName>
          </TableColumnItem>
        </TableColumnItems>
      </TableRowEntry>
    </TableRowEntries>
  </TableControl>
</View>

所以你得到一个 table,其中只有一个列包含 Guid 属性.

如果你这样做,你会得到完全相同的结果:

PS> [Guid]::NewGuid() | Format-Table -Property "Guid"

Guid
----
ee737b6f-7f68-4015-8841-1278b37a6420

如果您想将 default 格式化命令的结果捕获到字符串变量中(例如写入日志文件),您可以这样做:

PS> $text = [Guid]::NewGuid() | Out-String
PS> $text

Guid
----
ee737b6f-7f68-4015-8841-1278b37a6420

第 2 部分

Why is Convert-RawToGuid not getting a "pipelined" string from Convert-GuidToRaw? Swapping the order of the pipeline as below, causes the expected behaviour:

我无法重现您的问题,但以下对我有用(需要注意的是我找不到带有 class GuidConverter.Core.GuidConverter 的库,所以我假设它是一个私有实现,我使用默认的字符串格式代替 - 例如:“024673b5-9c65-447a-be87-dae5f11f5142”)。您可以在本地尝试此操作,看看您是否得到相同的结果或您报告的问题,然后从那里开始...

  • 在 Visual Studio
  • 中使用 .Net Framework 4.7.2 创建新的 C# Class 库解决方案
  • 添加对 Microsoft.PowerShell.5.1.ReferenceAssemblies NuGet 包的引用
  • 将下面的代码添加到新的 class 文件中并构建项目:
using System;
using System.Management.Automation;

namespace MyCmdlet
{

    [Cmdlet(VerbsData.Convert, "RawToGuid")]
    public class ConvertRawToGuidCommand : Cmdlet
    {
        [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
        public string Input { get; set; }
        protected override void ProcessRecord()
        {
            if (string.IsNullOrEmpty(this.Input) || this.Input.Length != 36)
            {
                throw new ArgumentException("Input must be a 36 character hex string");
            }
            var guid = new Guid(this.Input);
            WriteObject(guid);
        }
    }

    [Cmdlet(VerbsData.Convert, "GuidToRaw")]
    public class ConvertGuidToRawCommand : Cmdlet
    {
        [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
        public Guid Input { get; set; }
        protected override void ProcessRecord()
        {
            var raw = this.Input.ToString();
            WriteObject(raw);
        }
    }

}

如果我构建此代码,我可以从 PowerShell 引用它,并且以下代码有效:

PS> Import-Module ".\MyCmdlet.dll"
PS> New-Guid | Convert-GuidToRaw | Convert-RawToGuid

Guid
----
15591624-44c9-4c55-acd7-22b7a2d0bee0