PowerShell脚本模版

PowerShell 是一个强大的脚本环境,用于系统管理和其他任务自动化。

模块脚本模板

一个标准的 PowerShell 模块通常具有如下目录结构:

<ModuleName>/
├── <ModuleName>.psd1          # 模块清单文件(Module Manifest)
├── <ModuleName>.psm1          # 模块主脚本文件(可选,也可用多个 .ps1 文件)
├── Public/                # 公共函数目录(对外暴露的命令)
│   └── Get-HelloWorld.ps1
├── Private/               # 私有函数目录(内部使用,不导出)
│   └── Write-Log.ps1
└── main.ps1               # (可选)用于本地测试的入口脚本

💡 说明:

  • .psd1 是模块清单(manifest),用于声明模块元数据和导出内容。
  • .psm1 是模块脚本文件,可直接包含函数,或通过 Import-Module 加载 Public/Private/ 中的脚本。
  • main.ps1 并非模块必需文件,仅用于本地开发测试。

模块定义规范

1. 模块命名

  • 模块文件夹名、.psd1.psm1 文件名必须一致(如 MyModule)。
  • 推荐使用 PascalCase 命名(如 NetworkToolsDataProcessor)。

2. 模块清单(.psd1)关键字段

@{
    ModuleVersion     = '1.0.0'
    GUID              = 'a1b2c3d4-e5f6-7890-1234-567890abcdef'  # 使用 New-Guid 生成
    Author            = 'Cikaros'
    CompanyName       = 'CI科技有限责任公司'
    Description       = '简单Demo模板'
    PowerShellVersion = '5.1'  # 或 '7.0' 等
    FunctionsToExport = @('Get-HelloWorld')
    CmdletsToExport   = @()
    VariablesToExport = @()
    AliasesToExport   = @()
    RootModule        = '<ModuleName>.psm1'
    PrivateData       = @{
        PSData = @{
            Tags         = @('Demo', 'Tutorial')
            LicenseUri   = ''
            ProjectUri   = ''
        }
    }
}

3. 函数命名规范

  • 使用标准 PowerShell 动词 + 名词格式,如 Get-ItemSet-Config
  • 动词应来自官方批准动词列表:Get-Verb

4. 导出控制

  • 只有在 FunctionsToExport 中列出的函数才会被用户使用 Import-Module 后可见。
  • 私有函数(如日志、辅助函数)放在 Private/ 目录,不在清单中导出。

main.ps1 模板内容(用于本地测试)

注意:main.ps1 不是模块的一部分,仅用于开发时快速测试模块功能。

# main.ps1 - 本地测试脚本模板

# 设置错误处理
$ErrorActionPreference = 'Stop'

# 获取当前脚本所在目录
$ModuleRoot = Split-Path -Parent $MyInvocation.MyCommand.Path

# 构建模块路径
$ModuleName = 'MyModule'
$ManifestPath = Join-Path $ModuleRoot "$ModuleName.psd1"

# 如果模块已加载,先移除
if (Get-Module -Name $ModuleName -ErrorAction SilentlyContinue) {
    Remove-Module $ModuleName
}

# 从清单导入模块(开发模式)
Import-Module $ManifestPath -Force

# 调用模块中的函数进行测试
Get-HelloWorld

# 可选:列出模块导出的命令
Get-Command -Module $ModuleName

快速创建步骤

  1. 创建目录 MyModule
  2. 在目录中创建 Public/Private/ 子目录
  3. 编写函数脚本(如 Public/Get-HelloWorld.ps1
  4. 创建 MyModule.psm1,内容如下:
# MyModule.psm1
$Public  = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -ErrorAction SilentlyContinue )

foreach ($import in @($Public + $Private)) {
    try {
        . $import.FullName
    } catch {
        Write-Error "Failed to import function $($import.FullName): $_"
    }
}

Export-ModuleMember -Function $Public.BaseName
  1. 生成模块清单(首次):
New-ModuleManifest -Path .\MyModule.psd1 -RootModule MyModule.psm1 -FunctionsToExport 'Get-HelloWorld' -Author 'Your Name' -Description 'Demo module'
  1. 手动编辑 .psd1 补充 GUIDPowerShellVersion 等字段。
  2. 运行 main.ps1 测试模块。

安装与使用模块

将模块文件夹复制到 PowerShell 模块路径之一,例如:

  • 当前用户:$HOME\Documents\PowerShell\Modules\
  • 所有用户:$env:ProgramFiles\PowerShell\Modules\

然后在任意 PowerShell 会话中:

Import-Module MyModule
Get-HelloWorld

工具脚本模版

# 脚本名称: <脚本名称>
# 描述: <脚本描述>
# 版本: <版本>
# 作者: Cikaros
# 邮箱: Cikaros<at>qq.com
# 日期: <日期>
# 版权: (C) 2024 Your Company. All rights reserved.

# 定义参数
param (
    [string]$Param1,           # 第一个参数
    [string]$Param2,           # 第二个参数
    [switch]$Flag1,            # 开关参数1
    [switch]$Flag2,            # 开关参数2
    [int]$Number,              # 数字参数
    [string[]]$List,           # 列表参数
    [switch]$Help              # 显示帮助信息
)

# 显示帮助信息
function Show-Help {
    Write-Output "Usage: script.ps1 -Param1 <value> -Param2 <value> -Flag1 -Flag2 -Number <value> -List <value1,value2,...> -Help"
    Write-Output ""
    Write-Output "Parameters:"
    Write-Output "  -Param1        第一个参数"
    Write-Output "  -Param2        第二个参数"
    Write-Output "  -Flag1         开关参数1"
    Write-Output "  -Flag2         开关参数2"
    Write-Output "  -Number        数字参数"
    Write-Output "  -List          列表参数(逗号分隔)"
    Write-Output "  -Help          显示帮助信息"
}

# 检查是否需要显示帮助信息
if ($Help) {
    Show-Help
    exit
}

# 处理参数
if ($Param1) {
    Write-Output "Param1: $Param1"
}

if ($Param2) {
    Write-Output "Param2: $Param2"
}

if ($Flag1) {
    Write-Output "Flag1 is set"
}

if ($Flag2) {
    Write-Output "Flag2 is set"
}

if ($Number) {
    Write-Output "Number: $Number"
}

if ($List) {
    Write-Output "List: $($List -join ', ')"
}

交互类脚本模版

main.ps1

# 脚本名称: <脚本名称>
# 描述: <脚本描述>
# 版本: <版本>
# 作者: Cikaros
# 邮箱: Cikaros<at>qq.com
# 日期: <日期>
# 版权: (C) 2024 Your Company. All rights reserved.

# Powershell 2.0兼容
if ($PSVersionTable.PSVersion.Major -lt 3) {
    $PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
}
# =============全局定义================

# =============函数定义================
Import-Module -Name "Module1.psm1"
# Import-Module -Name "Module2.psm1"
# -------------------------------------
# function XXX { ... }
# =============脚本逻辑================
# 日志记录
Start-Transcript -Path "$PSScriptRoot\maker.log"
Clear-Host
# 窗口标题
$Host.UI.RawUI.WindowTitle = "Windows 映像创建器"
# 欢迎语
Write-Host "👏欢迎使用 Windows 映像创建器!"
# --------TODO 核心业务流程-------------

# --------TODO 核心业务流程 END---------
Stop-Transcript

Module1.psm1

function XXX-XXXXXX {
    <#
    .SYNOPSIS
        <提示信息>
    
    .DESCRIPTION
        <功能详情>
    
    .PARAMETER None
        <参数介绍>
    
    .EXAMPLE
        <示例代码>
    
    .OUTPUTS
        <返回值>
    
    .NOTES
        作者: Cikaros
        创建日期: 2024-9-19
        版本: 1.0
    #>
    ...
}

常用的功能型脚本模板

1. 获取操作系统信息

$os = Get-CimInstance -ClassName Win32_OperatingSystem
Write-Output "操作系统名称: $($os.Caption)"
Write-Output "版本: $($os.Version)"
Write-Output "安装日期: $($os.InstallDate)"

2. 检查服务状态

$serviceName = 'Spooler' # 打印队列服务
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($service) {
    Write-Output "服务 '$($service.Name)' 当前状态: $($service.Status)"
} else {
    Write-Output "未找到服务 '$serviceName'"
}

3. 创建目录并设置权限

$folderPath = 'C:\Temp\NewFolder'
if (-not (Test-Path -Path $folderPath)) {
    New-Item -ItemType Directory -Path $folderPath
    $acl = Get-Acl -Path $folderPath
    $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("Users", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow")
    $acl.SetAccessRule($rule)
    Set-Acl -Path $folderPath -AclObject $acl
    Write-Output "目录已创建并设置了权限"
} else {
    Write-Output "目录已存在"
}

4. 判断变量是否为空

$variable = 'Hello World'
if ([string]::IsNullOrEmpty($variable)) {
    Write-Output "变量是空的"
} else {
    Write-Output "变量不是空的,值为: $variable"
}

5. 判断文件是否存在

$filePath = 'C:\Path\To\File.txt'
if (Test-Path -Path $filePath) {
    Write-Output "文件存在"
} else {
    Write-Output "文件不存在"
}

6. 判断网络连接

$pingResult = Test-Connection -ComputerName 'www.example.com' -Count 1 -Quiet
if ($pingResult) {
    Write-Output "网络连接成功"
} else {
    Write-Output "无法连接到网络"
}

7. 使用 Switch 进行多条件判断

$number = 2
switch ($number) {
    1 { Write-Output "数字是1" }
    2 { Write-Output "数字是2" }
    3 { Write-Output "数字是3" }
    default { Write-Output "数字不在1, 2, 3中" }
}

8. 判断文件(夹)是否存在

$filePath = 'C:\Path\To\File.txt'
$folderPath = 'C:\Path\To\Folder'

if (Test-Path -Path $filePath) {
    Write-Output "文件存在"
} else {
    Write-Output "文件不存在"
}

if (Test-Path -Path $folderPath) {
    Write-Output "文件夹存在"
} else {
    Write-Output "文件夹不存在"
}

9. 判断是否为普通文件

$filePath = 'C:\Path\To\File.txt'

if (Test-Path -Path $filePath -PathType Leaf) {
    Write-Output "这是一个普通文件"
} elseif (Test-Path -Path $filePath -PathType Container) {
    Write-Output "这是一个文件夹"
} else {
    Write-Output "路径不存在"
}

10. 判断变量是否为空

$variable = $null

if ([string]::IsNullOrEmpty($variable)) {
    Write-Output "变量是空的"
} else {
    Write-Output "变量不是空的,值为: $variable"
}

11. 用户输入数据

$ userInput = Read-Host "请输入一些文本"
Write-Output "您输入的是: $userInput"

12. 判断指令是否存在

$command = 'Get-Process'

if (Get-Command -Name $command -ErrorAction SilentlyContinue) {
    Write-Output "命令 '$command' 存在"
} else {
    Write-Output "命令 '$command' 不存在"
}

13. 判断关键字是否存在

$text = "这是一个测试字符串"
$keyword = "测试"

if ($text -match $keyword) {
    Write-Output "关键字 '$keyword' 存在"
} else {
    Write-Output "关键字 '$keyword' 不存在"
}

14. 文件夹递归处理

$folderPath = 'C:\Path\To\Folder'

if (Test-Path -Path $folderPath) {
    Get-ChildItem -Path $folderPath -Recurse | ForEach-Object {
        if (Test-Path -Path $_.FullName -PathType Leaf) {
            Write-Output "文件: $($_.FullName)"
        } else {
            Write-Output "文件夹: $($_.FullName)"
        }
    }
} else {
    Write-Output "文件夹不存在"
}

15. 下载文件

$url = 'https://example.com/path/to/file.zip'
$destination = 'C:\Path\To\DownloadedFile.zip'

Invoke-WebRequest -Uri $url -OutFile $destination
Write-Output "文件已下载到: $destination"

16. 切换至管理员运行

# 检查是否以管理员身份运行
$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
if (-NOT $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Write-Warning "当前脚本未以管理员身份运行,正在尝试以管理员权限重新启动..."
    Start-Process Powershell -ArgumentList $PSCommandPath -Verb RunAs
    exit
} else {
    Write-Host "当前脚本已以管理员身份运行" -ForegroundColor Green
}

17. 选择驱动器

function Select-DriveLetter {
    <#
    .SYNOPSIS
        提示用户输入一个有效的 Windows 驱动器号(C-Z),并返回格式化后的驱动器号字符串。
    
    .DESCRIPTION
        此函数会循环提示用户输入一个驱动器号,直到输入的是字母 'C' 到 'Z'(大小写不敏感)。
        输入有效后,函数会将该字母与冒号 (:) 组合,形成标准的驱动器号格式(例如 "C:")。
        如果输入无效,函数会显示错误提示,并继续要求用户重新输入。
    
    .PARAMETER None
        此函数不接受任何参数。
    
    .EXAMPLE
        $drive = Select-DriveLetter
        # 用户被提示输入驱动器号
        # 如果用户输入 'd',则函数输出 "驱动器号设置为D:" 并返回 "D:"
    
    .OUTPUTS
        [string]
        返回一个表示驱动器号的字符串,格式为 "X:",其中 X 是 C 到 Z 之间的大写字母。
    
    .NOTES
        作者: Cikaros
        创建日期: 2024-9-19
        版本: 1.0
    #>
    do {
        $DriveLetter = Read-Host "请输入 Windows 映像的驱动器号"
        if ($DriveLetter -match '^[c-zC-Z]$') {
            $DriveLetter = $DriveLetter + ":"
            Write-Output "驱动器号设置为$DriveLetter"
        } else {
            Write-Output "⚠️驱动器号无效。请在 C 和 Z 之间输入一个字母。"
        }
    } while ($DriveLetter -notmatch '^[c-zC-Z]:$')
    return $DriveLetter
}

PowerShell脚本模版
https://blog.cikaros.top/doc/7da56ef8.html
作者
Cikaros
发布于
2024年11月15日
许可协议