第三章:语法基石——构建你自己的命令
在前两章的探索中,我们已经熟练地运用 PowerShell 提供的各种 Cmdlet 来执行任务,如同驾驶一辆性能优越的汽车。然而,真正的力量并不仅仅在于使用工具,更在于创造工具。本章,我们将从“驾驶员”的角色,向“工程师”的角色迈进,学习构建自定义逻辑和自动化方案所必需的语法基础。
我们将深入 PowerShell 作为一门“脚本语言”的本质,学习如何定义变量来存储信息,如何运用运算符进行计算与比较,以及如何通过流程控制语句赋予脚本“思考”和“决策”的能力。这就像学习建筑的蓝图语言,一旦掌握,你将能够从简单的命令组合,跃升到构建复杂、智能、自动化的宏伟建筑。
本章是连接“使用”与“创造”的桥梁。请以工程师的严谨和艺术家的想象力,来拥抱这些语法的基石。
3.1. 变量与数据类型:信息的容器
在任何编程语言中,最基本的需求都是存储和管理信息。一个脚本在执行过程中,需要临时记住一个文件名、一个用户列表、一个计算结果,或者一个系统状态。变量(Variable),就是我们用来存放这些信息的“容器”。而信息本身,也有着不同的种类和格式,这便是数据类型(Data Type)。
3.1.1. 声明与使用变量:以 $ 开头的魔法符号
在 PowerShell 中,变量的识别和使用规则非常简单直观:所有变量名都必须以美元符号 $ 开头。
-
声明与赋值
当你第一次给一个变量名赋值时,这个变量就被“声明”或创建了。赋值操作使用等号
=。# 声明一个名为 $message 的变量,并把字符串 "你好,PowerShell!" 存进去 $message = "你好,PowerShell!" # 声明一个名为 $userCount 的变量,并把数字 100 存进去 $userCount = 100 -
使用变量
要使用变量中存储的值,只需在代码中写下它的名字即可。
# 直接输出 $message 变量的内容 $message # 在字符串中嵌入变量 Write-Output "当前在线用户数:$userCount"当你在双引号
""括起来的字符串中包含变量时,PowerShell 会自动将其替换为变量所存储的值,这个特性被称为变量扩展(Variable Expansion)。 -
变量命名规则
- 必须以
$开头。 $后面可以跟字母、数字和下划线_。- 建议使用有意义的驼峰命名法(CamelCase),如
$userName或$logFilePath,以增强代码的可读性。
- 必须以
3.1.2. 核心数据类型:信息的不同形态
虽然 PowerShell 在赋值时会自动判断数据类型(这被称为“动态类型”),但理解其背后核心的数据类型,对于编写健壮的脚本至关重要。
-
字符串 (String) 用于表示文本信息。可以用单引号
' '或双引号"括起来。- 双引号
":支持变量扩展。 - 单引号
' ':纯粹的字面量,不会进行变量扩展。
$name = "世界" $greetingWithVar = "你好, $name!" # 输出: 你好, 世界! $greetingLiteral = '你好, $name!' # 输出: 你好, $name! - 双引号
-
整数 (Int) 用于表示没有小数部分的数字。
$age = 30 $fileCount = 1024 -
数组 (Array) 一个有序的、可以存储多个值的集合。通过逗号
,分隔元素来创建。# 创建一个包含多个服务器名称的数组 $servers = "SRV01", "SRV02", "WEB01", "DB01" # 访问数组中的元素(索引从 0 开始) $servers[0] # 输出: SRV01 $servers[2] # 输出: WEB01 # 获取数组的长度 $servers.Length # 输出: 4 -
哈希表 (Hashtable) 也称为关联数组或字典。它存储的是键-值对 (Key-Value Pair) 的集合,提供了一种通过“名称”来查找“值”的快速方式。哈希表使用
@{}语法创建,键值对之间用分号;分隔。# 创建一个描述用户信息的哈希表 $userProfile = @{ Name = "张三"; UserID = 1001; Department = "IT"; IsAdmin = $true # $true 是布尔类型(Boolean),表示“真” } # 通过键来访问值 $userProfile.Name # 输出: 张三 $userProfile["UserID"] # 输出: 1001 # 添加新的键值对 $userProfile.Email = "zhangsan@example.com"哈希表在组织结构化数据、创建自定义对象以及为 Cmdlet 提供参数时(我们将在后面学到)非常有用。
3.1.3. 类型转换与检查:确保容器里装的是对的东西
虽然 PowerShell 很灵活,但在某些情况下,我们需要精确地控制变量的数据类型,或者检查一个变量到底是什么类型。
-
强制类型转换 (Casting)
可以在变量或值前面加上用方括号
[]括起来的类型名称,来强制将其转换为该类型。# 将字符串 "123" 转换为整数 $numberAsString = "123" $realNumber = [int]$numberAsString # 将数字转换为字符串 $stringifiedNumber = [string]12345 # 也可以在声明变量时就“锁定”它的类型 [string]$name = "李四" # $name = 123 # 这行会报错,因为 $name 被锁定为只能存储字符串 -
检查变量类型:
.GetType()方法每个对象(包括变量中存储的值)都有一个名为
GetType()的方法,可以告诉你它的确切类型。$myVar = 123 $myVar.GetType() # 输出会告诉你它的类型是 System.Int32 (整数) $anotherVar = "Hello" $anotherVar.GetType() # 输出会告诉你它的类型是 System.String (字符串) $serverList = "SRV01", "SRV02" $serverList.GetType() # 输出会告诉你它的类型是 System.Object[] (对象数组)在编写需要处理不同类型输入的复杂脚本时,检查数据类型是一个非常重要的调试和验证手段。
通过本节的学习,我们掌握了在 PowerShell 中存储、分类和检验信息的基本功。变量是脚本的记忆,而数据类型则是这份记忆的格式。有了这些“容器”,我们就可以开始装入数据,并准备在下一节中,用“运算符”这套工具来对它们进行加工和处理了。
3.2. 运算符的舞台:执行计算与比较
如果说变量是脚本中静止的“名词”,那么运算符就是连接和操作这些名词的“动词”和“连词”。它们是脚本逻辑的核心,负责执行从简单的数学加法到复杂的模式匹配等各种操作。PowerShell 的运算符体系清晰明了,一旦掌握,就能对数据进行随心所欲的加工和判断。
3.2.1. 算术与赋值运算符:一切计算的基础
这是最基础、最常见的一类运算符,与我们在数学课上学到的概念非常相似。
-
算术运算符 (Arithmetic Operators) 它们用于执行基本的数学运算。
+(加法):10 + 5结果是15。当用于字符串时,它执行拼接操作:"Hello" + "World"结果是"HelloWorld"。-(减法):10 - 5结果是5。*(乘法):10 * 5结果是50。当用于字符串和数字时,它执行重复操作:"Abc" * 3结果是"AbcAbcAbc"。/(除法):10 / 5结果是2。%(取模/求余):10 % 3结果是1,因为 10 除以 3 商 3 余 1。
-
赋值运算符 (Assignment Operators) 它们用于给变量赋予新的值。
-
=(基本赋值):$a = 10将10存入变量$a。 -
+=,-=,*=,/=(复合赋值): 这些是“计算并赋值”的简写形式,让代码更简洁。$count = 10 $count += 5 # 等同于 $count = $count + 5。现在 $count 的值是 15。 $message = "Log: " $message += "Process started." # 现在 $message 是 "Log: Process started."
-
3.2.2. 比较运算符:逻辑判断的标尺
当我们需要判断两个值是否相等、哪个更大或更小时,就需要使用比较运算符。这是 PowerShell 语法一个非常鲜明的特点:比较运算符都以连字符 - 开头,这主要是为了避免与重定向符号 < 和 > 产生混淆。
-
相等性比较
-eq(Equal): 等于。 注意:不区分大小写。-ne(Not Equal): 不等于。-ceq(Case-sensitive Equal): 等于,区分大小写。-cne(Case-sensitive Not Equal): 不等于,区分大小写。
"apple" -eq "Apple" # 返回 $true "apple" -ceq "Apple" # 返回 $false 100 -ne 99 # 返回 $true -
大小比较
-gt(Greater Than): 大于。-ge(Greater than or Equal): 大于或等于。-lt(Less Than): 小于。-le(Less than or Equal): 小于或等于。
$age = 20 $age -gt 18 # 返回 $true $age -le 20 # 返回 $true -
集合包含关系
-contains: 包含。检查一个集合(如数组)是否包含某个元素。-notcontains: 不包含。-in: 在…之中。检查某个元素是否存在于一个集合中,语法上与-contains相反,更自然。
$servers = "SRV01", "SRV02", "WEB01" $servers -contains "SRV02" # 返回 $true "DB01" -in $servers # 返回 $false
3.2.3. 逻辑与模式匹配:构建复杂的判断逻辑
当我们需要组合多个条件,或者检查文本是否符合某种模式时,就需要用到逻辑运算符和模式匹配运算符。
-
逻辑运算符 (Logical Operators) 它们用于连接布尔值(
$true或$false),形成更复杂的逻辑表达式。-and: 逻辑与。只有当两边的表达式都为$true时,结果才为$true。-or: 逻辑或。只要两边的表达式中有一个为$true,结果就为$true。-not(或!): 逻辑非。反转一个布尔值,$true变$false,$false变$true。
$age = 25 $department = "IT" # 年龄大于 18 并且 部门是 "IT" ($age -gt 18) -and ($department -eq "IT") # 返回 $true # 部门是 "HR" 或者 部门是 "IT" ($department -eq "HR") -or ($department -eq "IT") # 返回 $true $isAdmin = $false -not $isAdmin # 返回 $true -
模式匹配运算符 (Matching Operators) 它们是处理字符串的强大工具,尤其在需要模糊匹配时。
-like: 通配符匹配。使用*(匹配任意多个字符) 和?(匹配单个字符) 进行简单的模式匹配。-notlike: 与-like相反。-match: 正则表达式匹配。使用功能更强大的正则表达式进行复杂的模式匹配。-notmatch: 与-match相反。
$filename = "SalesReport-2024-Q3.xlsx" # 使用 -like 检查文件名是否以 "SalesReport" 开头 $filename -like "SalesReport*" # 返回 $true # 使用 -like 检查文件名是否包含 "2024" $filename -like "*2024*" # 返回 $true # 使用 -match 和正则表达式检查文件名是否包含四位数字 # \d{4} 是一个正则表达式,表示“四个数字” $filename -match "\d{4}" # 返回 $true-like简单易用,适合日常的文件名筛选等场景。而-match则为需要精细文本解析的高级用户打开了通往正则表达式世界的大门。
掌握了 PowerShell 的运算符,就等于掌握了处理数据的核心技能。无论是简单的加减乘除,还是复杂的逻辑判断与文本匹配,这些运算符都为我们提供了清晰、一致且强大的工具。现在,我们的脚本不仅能存储信息,还能对信息进行深度加工和智能判断了。接下来,我们将学习如何利用这些判断结果,来控制脚本的执行流程,让它真正地“思考”起来。
3.3. 流程控制:让脚本“思考”
流程控制是所有编程语言的灵魂。它让我们的脚本摆脱了僵硬的、从上到下的顺序执行模式,赋予其根据条件变化做出不同响应、以及重复执行任务的能力。在 PowerShell 中,流程控制语句的语法清晰且功能强大,它们是构建任何有意义的自动化方案的绝对核心。
3.3.1. 条件判断:在十字路口做出选择
生活中充满了选择,脚本的世界也是如此。当脚本执行到某个节点,需要根据一个或多个条件来决定下一步该怎么走时,我们就需要条件判断语句。
-
If-ElseIf-Else:经典的条件分支这是最基础、最通用的条件判断结构。它的逻辑就像我们在日常生活中做决策一样:如果某个条件成立,就做A;否则,如果另一个条件成立,就做B;否则(如果以上条件都不成立),就做C。
语法结构:
if ( <条件1> ) { # 如果条件1为 $true,则执行这里的代码块 } elseif ( <条件2> ) { # 如果条件1为 $false,但条件2为 $true,则执行这里的代码块 } else { # 如果以上所有条件都为 $false,则执行这里的代码块 }示例:根据文件大小给出不同提示
$fileSize = (Get-Item -Path ".\mydata.csv").Length / 1MB # 获取文件大小并转换为MB if ($fileSize -gt 100) { Write-Host "文件过大 ($([math]::Round($fileSize, 2)) MB),建议分批处理。" -ForegroundColor Red } elseif ($fileSize -gt 10) { Write-Host "文件大小适中 ($([math]::Round($fileSize, 2)) MB)。" -ForegroundColor Yellow } else { Write-Host "文件很小 ($([math]::Round($fileSize, 2)) MB),可以快速处理。" -ForegroundColor Green }ElseIf和Else块都是可选的。你可以只有一个简单的If语句。 -
Switch:优雅地处理多重选择当你需要根据一个变量的多种不同取值来执行不同操作时,使用一长串的
If-ElseIf-Else会显得很笨拙。这时,Switch语句能提供一个更清晰、更优雅的解决方案。语法结构:
switch ( <要检查的变量或表达式> ) { <值1> { # 如果变量等于值1 # 执行这里的代码 } <值2> { # 如果变量等于值2 # 执行这里的代码 } # ...可以有任意多个分支 default { # 如果变量不匹配以上任何值 # 执行这里的代码 } }示例:根据服务状态执行不同操作
$serviceStatus = (Get-Service -Name "Spooler").Status switch ($serviceStatus) { "Running" { Write-Host "打印服务正在运行。" # 可以选择重启服务 # Restart-Service -Name "Spooler" } "Stopped" { Write-Host "打印服务已停止,正在尝试启动..." Start-Service -Name "Spooler" } "Paused" { Write-Host "打印服务已暂停。" } default { Write-Host "无法确定打印服务状态:$serviceStatus" } }Switch语句在处理枚举值、状态码等场景时,能让代码的可读性大大提高。
3.3.2. 循环的节拍:让任务自动重复
循环是自动化的核心。它让计算机能够不知疲倦地重复执行某个任务,无论是处理一个数组中的上千个元素,还是持续监控某个条件直到其满足为止。
-
ForEach-Object:管道中的循环利器 (别名:foreach)这是 PowerShell 中最常用、最具特色的循环方式。它通常用在管道的中间,对从上一个命令传递过来的每一个对象执行相同的操作。
语法结构:
<产生对象的命令> | ForEach-Object { # 这里的代码会对管道中传来的每一个对象执行一次 # 特殊变量 $_ 代表当前正在处理的对象 }示例:停止所有名为 “note” 的进程
# 获取所有名字以 "note" 开头的进程,然后对每一个进程执行 Stop-Process Get-Process -Name "note*" | ForEach-Object { Write-Host "正在停止进程 $($_.Name) (ID: $($_.Id))..." $_ | Stop-Process -Force }ForEach-Object完美体现了 PowerShell 的对象流思想,是处理集合的首选。 -
For循环:经典的计数循环当你需要精确地控制循环的次数,或者需要一个步进的计数器时,
For循环是最佳选择。它包含三个部分:初始化、循环条件和循环后操作。语法结构:
for ( <初始化>; <循环条件>; <每次循环后执行的操作> ) { # 只要循环条件为 $true,就重复执行这里的代码 }示例:打印从 1 到 5 的数字
for ($i = 1; $i -le 5; $i++) { # $i++ 是 $i = $i + 1 的简写 Write-Host "当前数字是: $i" } -
While循环:条件驱动的循环While循环会在其指定的条件为$true时,持续不断地执行代码块。它适用于那些不知道具体要循环多少次,但知道循环应该在何种条件下停止的场景。语法结构:
while ( <循环条件> ) { # 只要循环条件为 $true,就重复执行这里的代码 }示例:等待某个文件出现
$filePath = "C:\temp\signal.txt" Write-Host "正在等待文件 $filePath 出现..." while ( -not (Test-Path -Path $filePath) ) { # 文件不存在,暂停 5 秒 Start-Sleep -Seconds 5 Write-Host "..." -NoNewline } Write-Host "文件已找到!继续执行后续任务。"警告:使用
While循环时,务必确保循环体内部有逻辑能最终改变循环条件,使其变为$false,否则将导致“死循环”。
3.3.3. 循环控制:掌控循环的节奏
在循环执行的过程中,有时我们需要提前跳出整个循环,或者跳过当前这次循环的剩余部分,直接进入下一次。Break 和 Continue 就是为此而生的。
-
Break:立即终止循环Break语句会立即完全地跳出它所在的循环(For,While,ForEach-Object,Switch等)。示例:在数组中找到第一个匹配项后就停止
$users = "Alice", "Bob", "Charlie", "David" foreach ($user in $users) { Write-Host "正在检查用户: $user" if ($user -eq "Charlie") { Write-Host "已找到目标用户 Charlie!" break # 立即跳出 foreach 循环 } } -
Continue:跳过本次,继续下次Continue语句会立即停止当前这次循环的执行,并直接跳转到下一次循环的开始。示例:只处理数组中的特定用户
$users = "Alice", "Bob", "Charlie_admin", "David" foreach ($user in $users) { if ($user -like "*_admin") { Write-Host "跳过管理员用户: $user" continue # 跳过本次循环的剩余部分,直接检查下一个用户 } # 这部分代码只有在用户不是管理员时才会执行 Write-Host "正在为普通用户 $user 分配资源..." }
恭喜!至此,我们已经全面掌握了 PowerShell 的三大语法基石。我们学会了用变量存储数据,用运算符处理数据,用流程控制来指挥数据。你现在所拥有的,已经不仅仅是执行命令的能力,而是真正意义上“编写程序”的能力。你已经可以构建出能够适应变化、处理批量任务、具备初步智能的自动化脚本了。
从下一章开始,我们将带着这些强大的“内功”,重返“招式”的修炼,学习更高级的 Cmdlet 运用和数据处理技巧,将我们的脚本打磨得更加专业和强大。