我不确定我编写了多少次脚本来为微软生成服务器列表 远程桌面连接管理器(RDCMAN)。我发现写出许多不同的方式是一个非常简单的脚本。编写特定于域的语言(DSL)以生成RDCMAN文件可能不是此问题的最佳解决方案,但生成RDCMAN文件是第一个DSL的好项目。

这是一个系列中的第二个帖子,涵盖了DSL是什么以及如何编写一个。

指数

我们在建造什么?

RDCMan允许您组组并组织您的服务器连接。配置将保存到RDG文件中。如果打开文件,您将找到一个相当简单的XML文件。您在RDCMAN中构建的层次结构反映在XML结构中。

示例RDG文件

以下是我们将与之合作的示例配置。

RDCMAN样本

这里是生成的configuraiton文件。

<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
  <file>
    <credentialsProfiles />
    <properties>
      <expanded>True</expanded>
      <name>rdcman</name>
    </properties>
    <group>
      <properties>
        <expanded>True</expanded>
        <name>GroupATX</name>
      </properties>
      <group>
        <properties>
          <expanded>True</expanded>
          <name>GroupDMZ</name>
        </properties>
        <server>
          <properties>
            <name>ServerDMZ01</name>
          </properties>
        </server>
        <server>
          <properties>
            <name>ServerDMZ02</name>
          </properties>
        </server>
      </group>
      <group>
        <properties>
          <expanded>False</expanded>
          <name>GroupInternal</name>
        </properties>
      </group>
    </group>
  </file>
  <connected />
  <favorites />
  <recentlyUsed />
</RDCMan>

我创建了这个样本,深度为它,所以我们可以看到层次结构和多个元素是如何处理的。我们在这里有很多好的信息。它看起来像一个组元素可以包含多个组或服务器元素。每个组或服务器元素都有一个关于该项目的所有元数据的属性元素。

这些是我们今天将产生的数据的关键数据。

<server>
  <properties>
    <name>ServerDMZ02</name>
  </properties>
</server>

<group>
  <properties>
    <expanded>True</expanded>
    <name>GroupATX</name>
  </properties>
  <!-- group or server elements -->
</group>

该文件中有很多东西,但我认为我们可以在另一个时间摘要它。我觉得它的其余部分只是脚手架抓住这些元素。现在我们知道我们正在创建的内容,我们可以构建一些功能。

get-rdcserver

The Server element looks really easy. We just need a function that returns that chunk of XML with the correct server name.

    function get-rdcserver
    {
        param($ComputerName)
        @"
        <server>
          <properties>
            <name>$ComputerName</name>
          </properties>
        </server>
    "@
    }

这将是一个很好的开始,但我想云苏有点洒给我们更多的灵活性。以下是我们将使用前进的完整高级功能。

function Get-RdcServer
{
    [CmdletBinding()]
    param(
        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true,
            Position = 0
        )]
        [string[]]
        $ComputerName
    )
    process
    {
        foreach($node in $ComputerName)
        {
            @"
      <server>
        <properties>
          <name>$node</name>
        </properties>
      </server>
"@
        }
    }
}

I added pipeline and multiple $ComputerName support. This will add a lot of value to this command. Right now, I would expect we could use it like this.

get-rdcserver -ComputerName Server1
Get-RdcServer -ComputerName Server2

Get-RdcServer -ComputerName Server3,Server4

Get-Content -Path $path | Get-RdcServer

我会向您展示一下如何进入我们的DSL,但首先我们需要一个命令。

get-rdcgroup.

The Group element will be more interesting because we need a way for it to contain other servers or groups. In our example, the child items will either be function calls to get-rdcserver or calls to this new function get-rdcgroup.. We will use a [ScriptBLock] to hold these child items.

以下是我们创建组的功能。

function Get-RdcGroup
{
    [CmdletBinding()]
    param(
        [Parameter(
            Mandatory = $true,
            Position = 0
        )]
        [string]
        $GroupName,

        [Parameter(
            Mandatory = $true,
            Position = 1
        )]
        [scriptblock]
        $ChildItem
    )
    process
    {
        @"
    <group>
      <properties>
        <name>$GroupName</name>
      </properties>
"@
       $ChildItem.Invoke()

        '    </group>'
    }
}

First I specified two parameters. The first one $GroupName, will be the name of the group and the $ChildItem will contain our child items.

The body is really simple in this one. I have two strings that I let fall to the pipeline and I execute that $ChildItem. Executing the [ScriptBlock] will run any commands that we place in there.

现在,这个函数可以这样使用:

get-rdcgroup. -GroupName 'ATX' -ChildItem {
    Get-RdcServer -ComputerName 'Server1'
}

看起来像一个dsl

这是我们开始看到这看起来像DSL的地方。因为我们使用了位置参数,我们可以重写这样的命令:

get-rdcgroup. ATX {
    Get-RdcServer Server1,Server2
}

There is one more little known trick we can use here. Any command that is defined with the Get verb will have an automatic weak alias that is just the noun. You can run Service and Get-Service will be called. We could just rename our commands or even create aliases, but I find this to be a handy shortcut.

RdcGroup GroupATX {
    RdcServer Server1
    RDCServer Server2
}

我们还可以在我们的脚本块内放置组。这应该让我们使用我们的DSL重新创建原始示例。

RdcGroup GroupATX {
    RdcGroup GroupDMZ {
        RdcServer ServerDMZ01
        RdcServer ServerDMZ02
    }
    RdcGroup GroupInternal {
    }
}

当我们执行该时,它会生成内XML,该XML定义具有正确层次结构的组和服务器。

接下来是这个模块

我们此时只有1/2个解决方案。即使格式化不好,内部XML对于我们需要的是有效的。我没有像你一样缩进的孩子群体将正常XML,但RDCMAN不在乎。

We also need to build the outer XML of the file. This would built like our Get-RdgGroup function.

RDGMAN有很多设置和配置选项,我们可以实现。我拍了最小的设置以保持这个例子简单。

我已经涵盖了我想要的DSL实现,但我可以旋转另一个系列,其中我将其建在一个完整的功能模块中。

我们从哪里开始

现在我们知道DSL是什么,并将一些简单的东西放在一起,我将潜入一些在我的下一篇文章中建立更复杂的DSL解决方案的设计模式。