我的第一个 PowerShell 项目 - 如何管理脚本文件和相邻模块之间的互操作?

问题描述 投票:0回答:0

前言

我希望这会被标记为重复,但有太多跨越 PowerShell 十年历史的答案,我不确定哪些答案通过了集合,哪些已经过时。如果已经有这样的答案请见谅

问题

我应该如何构建项目?写好后如何导入?理想情况下,这些将是我经常使用但想与同事共享的脚本,因此它应该与 Git 兼容,和/或能够发布到私有 Artifactory 实例。我也有兴趣使它成为一个预编译的 C# 项目,但这是一个高级主题,在我得到一些简单的脚本之前我不想探索它。

代码示例

到目前为止我所拥有的是这样的:

~/My Documents/PowerShell (as found in $Env:PSModulePath)
|   Microsoft.PowerShell_profile.ps1
|
\---Modules
    +---Docker
    |   |   Docker.psd1
    |   |
    |   +---classes
    |   |       DockerContainerHealth.ps1
    |   |       DockerContainerIsolation.ps1
    |   |       DockerContainerStatus.ps1
    |   |
    |   \---public
    |           Remove-DockerContainer.ps1
    |           Remove-DockerImage.ps1
    |
    +---FileSystem
    |   |   FileSystem.psd1
    |   |
    |   \---public
    |           Test-PathLike.ps1
    |
    +---Utilities
    |   |   Utilities.psd1
    |   |
    |   \---public
    |           Append-StringBuilder.ps1
    |
    \---WSL
        |   WSL.psd1
        |
        \---public
                Invoke-OpenPgp.ps1

~/我的文档/PowerShell/Modules/Docker/public/Remove-DockerContainer.ps1

function Remove-DockerContainer
{
  [CmdletBinding()]
  param(
    [System.String]$ContainerId,
    [System.String]$ContainerName,
    [System.String]$ContainerLabel,
    [System.Int32]$ContainerExitCode,
    [DockerContainerStatus]$ContainerStatus,
    [System.String]$ImageName,
    [System.String]$ImageId,
    [System.String]$BeforeContainerId,
    [System.String]$SinceContainerId,
    [System.String]$VolumeName,
    [System.String]$NetworkName,
    [System.String]$Port,
    [DockerContainerHealth]$ContainerHealth,
    [DockerContainerIsolation]$ContainerIsolation,
    [switch]$IsTask,
    [switch]$ShowStopped,
    [switch]$ShowLast,
    [System.Int32]$Limit
  )

  $args = New-Object System.Text.StringBuilder
  $args.Append('docker container ls -q')

  # Either shows last
  if($ShowLast) { $args.Append(' -l') }
  # Or list N most-recent
  elseif ($Limit -ne $null) { $args.Append(" -n $Limit") }

  if($ContainerId -ne $null) { $args.Append("-f id=$ContainerId") }
  if($ContainerName -ne $null) { $args.Append(" -f name=$ContainerName") }
  if($ContainerLabel -ne $null) { $args.Append(" -f label=$ContainerLabel") }
  if($ContainerExitCode -ne $null) { $args.Append(" -f exited=$ContainerExitCode") }
  if($ContainerStatus -ne $null) { $args.Append(" -f status=$([DockerContainerStatus].GetEnumName($ContainerStatus).ToLower())") }
  if($ImageName -ne $null) { $args.Append(" -f ancestor=$ImageName") }
  if($ImageId -ne $null) { $args.Append(" -f ancestor=$ImageId") }
  if($BeforeContainerId -ne $null) { $args.Append('f').Append("before=$BeforeContainerId") }
  if($SinceContainerId -ne $null) { $args.Append(" -f since=$SinceContainerId") }
  if($VolumeName -ne $null) { $args.Append(" -f volume=$VolumeName") }
  if($NetworkName -ne $null) { $args.Append(" -f network=$NetworkName") }
  if($Port -ne $null) { $args.Append(" -f expose=$Port") }
  if($ContainerHealth -ne $null) { $args.Append(" -f health=$([DockerContainerHealth].GetEnumName($ContainerHealth).ToLower())")}
  if($ContainerIsolation -ne $null) { $args.Append(" -f isolation=$([DockerContainerIsolation].GetEnumName($ContainerIsolation).ToLower())") }
  if($IsTask) { $args.Append(' -f is-task=true') }

  if($ShowStopped -eq $false) { $ShowStopped = $ContainerExitCode -ne $null -or $ContainerStatus -ne $null }

  
  foreach($containerId in (Invoke-Expression "$args"))
  {
    docker wait "$containerId"
    docker container rm "$containerId"
  }
}

~/我的文档/PowerShell/Modules/Utilities/public/Append-StringBuilder.ps1

function Append-StringBuilder
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [System.Text.StringBuilder]$Builder,
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$false)]
        [System.String]$Value
    )

    process
    {
        if($Value -ne $null -and $Value.Count -gt 0) { $Builder.Append($Value) }
        return $Builder
    }
}

function Aggregate-String
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$false)]
        [System.Text.StringBuilder]$Builder,
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [System.String]$Value
    )

    begin
    {
        if($Builder -eq $null) { $Builder = New-Item System.Text.StringBuilder }
    }

    process
    {
        $Builder | Append-StringBuilder $Value
    }
}

Export-ModuleMember -Function Append-StringBuilder
Export-ModuleMember -Function Aggregate-String

问题

  1. 我应该如何构建项目文件夹? 正如问题所说,这是我的第一个 PowerShell 项目。我熟悉语法,并花了很多时间查看 PowerShell about* 文档。也就是说,它没有讨论模块应该如何构建。我在 r/PowerShell 上找到了一个 有主见的指南Reddit 线程提供,但该帖子是 4 年前的,模板项目于去年存档,这意味着某些信息可能已过时。
  2. 如何导出类和枚举? PowerShell 的文档明显遗漏了如何导出类和枚举。在 PowerShellTemplate 项目中,没有用于导出枚举的显式语法 如此处所示 但后来提到文件名应以数字为前缀以表示它为某些 PowerShell cmdlet 初始化的顺序
    Optimize-Module
    我可以在任何地方都可以找到记录。
  3. 如何在模块之间导入,例如将
    Utilities
    导入
    Docker
    您可能会注意到所提供的
    Remove-DockerContainer
    示例中的一个常见模式,这就是我写
    Append-StringBuilder
    的原因。我想导入它,但类似于 Microsoft 缺乏关于如何构建项目的信息,关于如何进行相对导入的信息也很少甚至没有。它说“当
    <module-name>
    是路径时,路径可以是完全限定的或相对的。相对路径是相对于包含 using 语句的脚本解析的。”这省略了我是否应该导入脚本
    using module ../Utilities/Append-StringBuilder.ps1
    或模块
    using module ../Utilities
    或模块清单
    ../Utilities/Utilities.psd1
    .

结束

由于我是 PowerShell 项目的新手,任何参考资料都将不胜感激。我厌倦了编写一次性脚本并四处寻找我保存它们的位置,所以我正在努力养成一种更好的习惯,将脚本形式化以供重复使用。我已经为 Bash 做了一段时间,并且想改善我在 Windows 上的体验。

powershell directory-structure
© www.soinside.com 2019 - 2024. All rights reserved.