PowerShell 脚本编程指南

PowerShell 是一种基于.NETFramework 的命令行 shell 和脚本语言,它提供了访问 Windows 操作系统的许多强大功能,这些功能在传统的命令提示符(CMD)中难以访问。PowerShell 的核心是基于小型功能单元,称为 Cmdlets(命令-单元)。可以通过 Microsoft Technet 页面获取 Cmdlets 的列表。

PowerShell 脚本使用的脚本语言具有自己的语法。本章将简要概述语法,并解释 PowerShell 脚本中可能隐藏的一些陷阱。

变量以 '$' 开头定义:

$programFilesX86 = (${env:ProgramFiles(x86)}, ${env:ProgramFiles} -ne $null)[0]

上述示例将变量 "programFilesX86" 设置为 x86 程序的程序文件文件夹路径。如果 x86 平台上不存在该文件夹,则在 x86 文件夹不可用时,变量将设置为普通程序文件目录。

常量也以 '$' 开头定义:

$CONSTVAL = "This value can't be changed" Set-Variable CONSTVAL -option ReadOnly

尽管解释器规范不需要这样做,但建议将常量全部大写,以区分变量。

PowerShell脚本文件是解释执行的,如果在调用之前没有读取到某个方法,PowerShell 将遇到错误。避免这种错误陷阱的最佳方法是在文件顶部定义一个名为 "Main" 的方法,然后在文件末尾定义一个跳转到这个方法的跳转:

function main{ # 主函数,如经典结构化程序中所知。 myFunction } function myFunction{ Write-Host "myFunction was called" } main # 跳转到执行 main

下面的例子本应做同样的事情,但由于在调用时 "myFunction" 函数尚未被解释,因此无法运行:

myFunction # 错误 - myFunction 尚未读取,但已经被调用 function myFunction{ Write-Host "myFunction was called" # 永远不会到达 }

基本命令

与命令提示符一样,PowerShell提供了大量的 Cmdlets 来访问文件系统并修改文件和目录。

可以使用 Copy-Item Cmdlet 复制文件,其中第一个参数指定源文件,第二个参数指定目标文件的完整路径:

Copy-Item -Path "C:\Source\Powershell" -Destination "C:\Testumgebung"

通过在命令末尾添加 -Recurse 参数,可以使复制过程递归:

Copy-Item -Path "C:\Source\Powershell" -Destination "C:\Testumgebung" -Recurse

可以使用 Get-Item Cmdlet 获取文件或文件夹信息:

Get-Item -Path "C:\"

要获取路径的子项信息,可以使用带有通配符的 Get-Item 或 Get-ChildItem:

Get-Item -Path "C:\*" Get-ChildItem -Path "C:\"

两个 Cmdlets 都返回 C:\ 的子项详细信息:

可以使用 Remove-Item Cmdlet 删除文件或目录:

Remove-Item -Path "C:\Source\Test1\*.txt" -Recurse -Force

-Recurse 参数只能应用于文件,而 -Force 也删除锁定的文件。在上述参数化中,任何 *.txt 文件将从 C:\Source\Test1 及其子文件夹中删除。

可以使用 Copy-Item Cmdlet 复制文件或目录:

Copy-Item -Path "C:\Source\*.txt" -Destination "C:\Source\Textfiles" -Recurse

此命令将从 C:\Source 及其子文件夹复制任何 *.txt 文件到 C:\Source\Textfiles,子文件夹结构保持不变。

PowerShell 提供了大量的 Cmdlets 来帮助分析和操作正在运行的进程。

可以使用几乎任何可能的选项启动新进程:

Start-Process powershell.exe

这将启动一个新的 PowerShell 实例。Start-Process 是一个强大的 Cmdlet,提供了许多参数。可以在 Microsoft Technet 主页上找到所有参数的列表。

要获取系统上所有正在运行的进程列表,可以使用 Get-Process:

Get-Process

这将显示所有正在运行的进程列表,包括一些附加信息:

当然,可以通过其名称搜索特定进程:

Get-Process powershell

这种行为可以通过搜索多个进程名称来扩展,例如 PowerShell 和 Windows Explorer:

Get-Process powershell,explorer

明白了 - 可以以这种方式检查任何数量的进程名称。如果正在寻找一个没有运行的进程,PowerShell 将返回一个错误(例如抛出)。可能还不知道的是,进程名称允许使用通配符,意味着:

Get-Process power*

将返回每个以 "power" 开头的正在运行的进程。那些碰巧知道 WMI 类 Win32_Process 的人可能已经意识到,所有演示的内容都可以通过这个类轻松完成。但 PowerShell 可以做更多:WMI 不会公开诸如公司或产品版本之类的属性,但 PowerShell 会。以下示例中,Get-Process 通过 Select-Object Cmdlet 过滤,除了进程名称、产品版本和公司之外的任何信息:

Get-Process powershell | Select-Object name,productversion,company

现在可能会问自己,应该如何知道哪些属性可以通过 get-process 获得,不是吗?可以自己找出来。以下行通过 get-member 过滤 get-process 并显示所有可用成员:

Get-Process | Get-Member

自己试试吧 - 会得到一个非常长的成员列表,可以自由使用它们。

当然,迟早会遇到需要停止正在运行的进程的情况 - 使用 stop-process Cmdlet 来完成它。它接受进程 ID 或进程名称作为参数:

Stop-Process 23498 Stop-Process -processname notepad

以下函数使用.NETSystem.Diagnostics 命名空间启动进程。当然,也可以简单地使用 Start-Process Cmdlet 来实现,但它为提供了在 PowerShell 脚本中使用 .NET 类的正确语法的一个很好的开始。

function StartProcess([string]$filePath, [array]$arguments, [bool] $waitForExit) { $pinfo = New-Object System.Diagnostics.ProcessStartInfo # 创建一个新的进程启动信息对象 $pinfo.FileName = $filePath # 将文件名设置为 filePath 参数 $pinfo.RedirectStandardError = $true # 标准错误和标准输出将被重定向 $pinfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pinfo.Arguments = $arguments # 将参数设置为指定的 $arguments $p = New-Object System.Diagnostics.Process # 创建一个进程 $p.StartInfo = $pinfo # 将启动信息分配给进程 $p.Start() | Out-Null # 启动进程 IF($waitForExit){ # 如果指定,则等待进程退出 $p.WaitForExit() } }

实用工具

本章提供了一些编写的PowerShell函数示例。所有这些函数都提供了现有 Cmdlets 没有提供的功能,但在许多情况下仍然很有用。

仅创建目录会在目录已经存在时抛出错误。这个小助手函数通过仅在目录不存在时创建目录来避免这个问题:

function CreateDirectoryIfNotExisting([string]$dirPath){ IF(!(Test-Path -Path $dirPath)){# 检查目录是否存在 New-Item -ItemType directory -Path $dirPath # 如果不存在,则创建它 } }

如果指定的文件在文件系统中缺失,则此函数将中止脚本执行。

function AbortIfFileMissing ([string]$filePath, [string]$name) { IF(!(Test-Path $filePath)){# 如果找不到文件则抛出 Throw ("ERROR: The File $name was not found! Expected path: $filePath") } ELSE{ Write-Host ("File " + $name + " found. It's located at " + $filePath) } } function AbortIfDirectoryMissing ([string]$dirPath, [string]$name) { IF(!(Test-Path $dirPath)){# 如果找不到目录则抛出 Throw ("ERROR: The directory $name was not found! Expected path: $dirPath") } ELSE{ Write-Host ($name + " found. It's located at " + $dirPath) } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485