Powershell 进阶语(三)

目录

PowerShell 管道

管道输出

PowerShell 命令不会生成文本作为输出,而是会生成对象,对象是描述内存中数据结构的通用词。

运行 Get-Service 命令时,它会返回服务对象的集合,每个对象都包含 Name、DisplayName 和 Status 等名称的属性。

控制管道输出的格式设置

格式设置 cmdlet 为:

  • Format-List

  • Format-Table

  • Format-Wide

  • Format-Custom

    Format-Custom cmdlet 需要创建定义格式的自定义 XML 配置文件。 该 cmdlet 不经常使用。

Format-List

Format-List cmdlet 将命令的输出格式化为一个简单的属性列表,其中每个属性显示在一个新行上。

如果将输出传递给 Format-List 的命令返回多个对象,则会为每个对象显示单独属性列表。

当命令返回大量很难以表格格式查看的属性时,列表格式尤其有用。

复制代码
Format-List cmdlet 的别名是 fl。

Format-Table

Format-Table cmdlet 将输出格式化为表格,其中每一行表示一个对象,每一列表示一个属性。

这个输出的效果和默认的没啥效果,多了一个就是你可以选择参数来进行定义你输出的效果,下面介绍参数↓↓↓

可使用多种参数修改此格式,例如:

  • -AutoSize。 此参数可根据数据的宽度来调整列的大小和数量。 在 Windows PowerShell 5.0 及更高版本中,-AutoSize 默认设置为 true。 在更低版本的 Windows PowerShell 中,默认值可能会截断表中的数据。

  • -HideTableHeaders。 此参数会从输出中移除表标头。

  • -Wrap。 此参数会使超出列宽的文本换行到下一行。

    Format-Table cmdlet 的别名是 ft。

Format-Wide

Format-Wide cmdlet 的输出是单个列表中分多列显示的单个属性。

这个其实只需要知道:使用 -Property 参数,指定一个属性去进行多个列展示,不用单个列展示这么难看

下面展示的就是服务的所有名字,但是我们是用3列进行展示

powershell 复制代码
Get-Service | fw -Property Name -Column 3

管道选择、排序和度量对象

排序和分组

Sort-Object

Sort-Object 命令接受一个或多个属性名作为排序依据, 默认情况下,命令按升序排序

powershell 复制代码
Get-Service | Sort-Object --Property Name --Descending
Get-Service | Sort Name --Desc 
Get-Service | Sort Status,Name

默认情况下,字符串属性的排序不考虑大小写。

(但是Sort-Object 也有参数去支持指定区分大小写的排序、特定区域性的排序规则和其他选项)

Format 的 GroupBy分组

Format-List、Format-Table 和 Format-Wide 格式设置 cmdlet 具有接受属性名的 -GroupBy 参数。 通过使用 -GroupBy 参数,可以按指定属性对输出进行分组。

注意:这里说的是Format的几个格式设置的分组,所以要用格式cmdlet然后再使用这个GroupBy参数

powershell 复制代码
Get-Service | Sort-Object Status,Name | fw -GroupBy Status

-GroupBy 参数的运行方式与 Group-Object 命令类似。 Group-Object 命令接受管道输入,让你可以更好地控制对象分组。 Group-Object 具有别名 group。

度量管道中的对象

Measure-Object 默认情况下,该命令会对集合中的对象数进行计数,并生成包含计数的测量对象。

使用 Measure-Object 的 -Property 参数可指定单个属性,该属性必须包含数值。 随后,可以添加 -Sum、-Average、-Minimum 和 -Maximum 参数,以计算指定属性的这些聚合值。

(通常可注意到 -Sum、-Average、-Minimum 和 -Maximum 参数被截断为 -Sum、-Ave、-Min 和 -Max)

以下命令计算文件夹中的文件数,并显示文件大小的最小、最大和平均值:

(Recurse递归读取)

powershell 复制代码
Get-ChildItem -File -Recurse | Measure -Property Length -Sum -Average -Minimum -Max

效果如下图:

Select-Object

复制代码
Select-Object 命令具有别名 Select。

这个命令比较简单,用法如下:

用Property指定要选择的对象即可,多个就用逗号隔开

powershell 复制代码
Get-Process | Select-Object -Property 列名1,列名2
  • 选择最少虚拟内存使用量排名前 10 的进程
powershell 复制代码
Get-Process | Sort-Object --Property VM | Select-Object --First 10
  • 选择最后 10 个正在运行的服务并按名称排序
powershell 复制代码
Get-Service | Sort-Object --Property Name | Select-Object --Last 10
  • 选择 CPU 用量最少的五个进程,并跳过使用最少 CPU 的那一个进程
powershell 复制代码
Get-Process | Sort-Object --Property CPU --Descending | Select-Object --First 5 --Skip 1

Unique去重

只需要在指定对象后添加多一个-Unique参数即可

  • 显示某个用户在每个部门中的用户信息
powershell 复制代码
Get-ADUser -Filter * -Property Department | Sort-Object -Property Department | Select-Object Department -Unique

Property

没啥好说的,就是select-object 的 参数Property 指定显示的进程属性即可

  • 显示一个表,其中包含本地计算机上运行的所有进程的名称、进程 ID、虚拟内存大小、分页内存大小和 CPU 使用率:
powershell 复制代码
Get-Process | Select-Object --Property Name,ID,VM,PM,CPU | Format-Table
  • -Property 参数适用于 -First 或 -Last 参数。 以下命令返回具有最大 CPU 使用率的 10 个进程的名称和 CPU 使用率:
powershell 复制代码
Get-Process | Sort-Object --Property CPU --Descending | Select-Object --Property Name,CPU --First 10

自定义属性并设置表达式与格式

  • 定义属性的名字:
    • label、l、name 或 n:这指定计算属性的标签或名称。 由于小写字母 l 在某些字体中类似于数字 1,因此请尝试使用 name、n 或 label。
  • 设置属性计算的式子:
    • expression 或 e:这将指定设置计算属性值的表达式。
powershell 复制代码
Get-Process |
Select-Object Name,ID,@{n='VirtualMemory';e={$PSItem.VM}},@{n='PagedMemory';e={$PSItem.PM}}

$PSItem 是由 Windows PowerShell 创建的特殊变量。 它表示通过管道传输到 Select-Object 命令中的任何对象。 在上一个示例中,这是一个 Process 对象。 $PSItem 之后的句点允许访问对象的单个成员。 在此示例中,一个计算属性使用 VM 属性,另一个使用 PM 属性。

当我们输入正常的Get-Process 并获取他的所有属性的时候你会看到VM这个属性,所以我们$PSItem.VM是能够获取到的

检查一下是否有VM

powershell 复制代码
Get-Process | Get-Member | Select-Object -Property Name | Format-Wide -Column 5

下图可以看到确实有VM属性


  • 为什么用@{}
    解释:还记不记得之前创建空数组是用@(),这里是创建hash表,所以用@{},n是属性名,对应键,e是计算式子相当于值,所以说我们还能知道在自定义的时候不能定义同一个名字的属性

计算

你可能想要修改前面的命令以显示内存值(以兆字节 (MB) 为单位)。 PowerShell 将缩写 KB、MB、GB、TB 和 PB 分别表示千字节、兆字节、千兆字节、兆兆字节和拍字节。 因此,可以按如下所示修改命令:

($PSItem是接收传过来的对象,$_也是一样的, 用的比较多的是$_,所以说我们可以在Get-Process出来的对象中一个个用$PSItem去取你想要的属性或者激活方法)

powershell 复制代码
Get-Process |
Select-Object Name,
              ID,
              @{n='VirtualMemory(MB)';e={$PSItem.VM / 1MB}},
              @{n='PagedMemory(MB)';e={$PSItem.PM / 1MB}}

生成的值有几个小数位,这在视觉上是不理想的。

若要改进输出,请进行以下更改:

powershell 复制代码
Get-Process |
Select-Object Name,
              ID,
              @{n='VirtualMemory(MB)';e={'{0:N2}' --f ($PSItem.VM / 1MB) -as [Double] }},
              @{n='PagedMemory(MB)';e={'{0:N2}' --f ($PSItem.PM / 1MB) -as [Double] }}

上一个示例中的语法可能看起来令人困惑,因为它包含许多标点符号。 从基本表达式开始:

powershell 复制代码
'{0:N2}' --f ($PSItem.VM / 1MB)

上一个表达式将 VM 属性除以 1 MB,然后将结果格式化为最多两个小数位的数字。 然后将该表达式放入哈希表中:

该哈希表创建名为 VirtualMemory(MB) 的自定义属性。

powershell 复制代码
@{n='VirtualMemory(MB)';e={'{0:N2}' --f ($PSItem.VM / 1MB) }}

从管道中筛选对象

比较运算符

操作 DESCRIPTION
-eq 等于
-ne 不等于
-gt 大于
-lt 小于
-le 小于或等于
-ge 大于或等于

在powershell中默认是不区分大小写,但是在比较的时候难免希望对大小写的区分

所以我们可以在操作中添加一个前缀c作为比较区分大小写的标志

powershell 复制代码
-ceq
-cne

不仅仅上述的运算符,其他也都可以加c

当然还有like也可以用来区分大小写去匹配,因为-like 运算符类似于 -eq
-clike

其他更高级的运算符存包括:

  • -in-contains 运算符,用于测试集合中是否存在对象。
  • -as 运算符,用于测试对象是否为指定类型。
  • 将字符串与正则表达式进行比较的 -match-cmatch 运算符。
  • 还包含许多运算符来反转比较的逻辑,例如 -notlike-notin

简单案例:

powershell 复制代码
PS C:\> 100 -gt 10 
True 
PS C:\> 'hello' -eq 'HELLO' 
True 
PS C:\> 'hello' -ceq 'HELLO' 
False

基本筛选器语法

Where-Object 命令及其别名 Where

  • 仅显示正在运行的服务的列表
powershell 复制代码
Get-Service | Where Status --eq Running

基本语法的限制

只能对单个比较使用基本语法。 例如,无法显示已停止且具有"自动"启动模式的服务列表,因为需要两个比较。

不能将基本语法用于复杂表达式。 例如,服务对象的 Name 属性由一串字符组成。 PowerShell 使用 System.String 对象包含该串字符,而 System.String 对象具有 Length 属性。 以下命令不适用于基本筛选语法:

powershell 复制代码
Get-Service | Where Name.Length --gt 5

目的是显示名称超过五个字符的所有服务。 但是,此命令永远不会生成输出。 一旦超过基本语法的能力,必须改用高级筛选语法。

高级筛选器语法

高级语法使用筛选器脚本

使用 -FilterScript 参数传递该脚本块

对于通过管道传递到命令的每个对象,筛选器脚本都会运行一次。 当筛选器脚本返回 True 时,该对象将作为输出传递到管道中,当筛选器脚本返回 False 时,将从管道中删除该对象。

以下两个命令具有相同的功能。 第一个命令使用基本语法,第二个命令使用高级语法执行相同的操作:

powershell 复制代码
Get-Service | Where Status --eq Running

Get-Service | Where-Object --FilterScript { $PSItem.Status --eq 'Running' }

-FilterScript 参数是位置参数,大多数用户会省略它。 大多数用户还会使用 Where 别名或 ? 别名,此名称长度更短。

推荐使用 $_ 变量而不是 $PSItem,因为在 Windows PowerShell 1.0 和 Windows PowerShell 2.0 中只允许使用 $_

以下命令执行与前两个命令相同的任务:

powershell 复制代码
Get-Service | Where {$PSItem.Status --eq 'Running'}

Get-Service | ? {$_.Status --eq 'Running'}

组合多个条件

高级语法允许通过使用 -and 和 -or 布尔值或逻辑运算符来组合多个条件。 下面是一个示例:

powershell 复制代码
Get-EventLog --LogName Security --Newest 100 |
Where { $PSItem.EventID --eq 4672 --and $PSItem.EntryType --eq 'SuccessAudit' }

逻辑运算符的任意一侧都必须是一个完整的比较式子,下面给几个错误示范,请不要犯错:

powershell 复制代码
Get-Process | Where { $PSItem.CPU --gt 30 --and VM --lt 10000 }

Get-Service | Where { $PSItem.Status --eq 'Running' --or 'Starting' }

True 或 False 的属性筛选使用技巧

Get-Process 生成的对象具有名为"Responding"的属性,此属性包含 True 或 False。

获取正在响应的进程的列表,可以使用以下命令之一:

powershell 复制代码
Get-Process | Where { $PSItem.Responding --eq $True }

Get-Process | Where { $PSItem.Responding }

在第一个命令中,特殊 shell 变量 $True 用于表示布尔值 True。 第二个命令未包含任何比较,但它是有效的,因为 Responding 属性已包含 True 或 False

这类似于反向逻辑,仅列出未响应的进程:

powershell 复制代码
Get-Process | Where { -not $PSItem.Responding }

在前面的示例中,-not 逻辑运算符将 True 更改为 False,并将 False 更改为 True。 因此,如果进程未响应,则其 Responding 属性为 False。 -not 运算符将结果更改为 True,这会使进程被传递到管道中,并包含在命令的最终输出中。

高级筛选不受简单筛选限制

现在再去根据字符长度来筛选就没问题了

powershell 复制代码
Get-Service | Where {$PSItem.Name.Length --gt 8}

优化筛选器性能


说是优化,但其实都是靠编写脚本自己的功底


Get-随便 对于下面两个示例,你认为哪一个速度更快?

powershell 复制代码
Get-随便 | Sort-Object --Property Letter | Where-Object --FilterScript { $PSItem.Color --eq 'Red' }

Get-随便 | Where-Object --FilterScript { $PSItem.Color --eq 'Red' } | Sort-Object --Property Letter

第二个命令速度更快,因为他先是移除了不要的再进行排序,这加速了排序,但是如果你排序了再移除就表示你排序排了没用的东西,然后还要移除,那就慢了。

再比如下面这个查找文件,一看就知道是第二个块了,内置的直接使用更快

powershell 复制代码
Get-ChildItem | Where { -not $PSItem.PSIsContainer }

Get-ChildItem -File

枚举

这里的枚举是说powershell取出来的对象在管道中每一个都传递给下一个cmdlet去操作。

比如:停止计算机上每个正在运行的记事本进程,则可以运行以下两个命令之一

powershell 复制代码
Get-Process --Name Notepad | Stop-Process

Stop-Process --Name Notepad

比如Get-Process 筛选了名字为Notepad的,那就可能会出现很多个进程,那给到管道后面的Stop-Process来说,他就是在枚举每一个Notepad进程然后执行停止进程操作

枚举管道对象语法

这里官网分了基本语法和高级语法,但其实枚举管道都支持

基本和高级的区分就是:基本的不用脚本,高级的用脚本块

查看下ForEach-Object的帮助文档:

可以看到有两个参数,一个是接脚本块的,一看就是高级用法

基本用法应该就是下面的-MemberName指定属性或方法名的了


基本语法:

两个常见别名: ForEach% 。 与 Where-Object 一样, ForEach-Object 具有基本语法和高级语法。

下面三个都一样,原理都是将Get-ChildItem取出来的对象 ([System.IO.FileInfo])进行For枚举出来然后参数-MemberName就是取属性或者方法,那我们给的Encrypt就是一个方法(加密),所以我们就相当于给当前遍历的对象执行了该方法,以此类推每一个对象都会执行一次。

powershell 复制代码
Get-ChildItem --Path C:\Encrypted\  -File | ForEach-Object  -MemberName Encrypt

# 使用了别名和忽略了参数
Get-ChildItem --Path C:\Encrypted\ -File | ForEach Encrypt

# 使用了别名和忽略了参数
Get-ChildItem --Path C:\Encrypted\ -File | % Encrypt

你可以特地去查看下是否有该方法:

powershell 复制代码
 Get-ChildItem | Get-Member -Name Encry*

高级语法:就是使用脚本块

使用高级语法加密一组文件

powershell 复制代码
Get-ChildItem --Path C:\ToEncrypt\ -File | ForEach-Object --Process { $PSItem.Encrypt() }

范围运算符是两个句点 (..),中间没有空格,range 运算符生成从 1 到 3 的整数对象,这 3 个对象通过管道传递给 ForEach-Object,迫使脚本块运行 3 次

powershell 复制代码
1..3 | ForEach-Object { Get-Random }

写入到文件

Out-File就相当于cmd里面的文本重定向运算符 >>>,这些运算符可作为 Out-File 的别名,管道末尾的大于号 (> ) 将输出定向到文件,从而覆盖内容,两个连续的大于号 (>>) 将输出定向到文件,从而将输出附加到文件中已有的任何文本。

例如:

下面这个虽然说输出到csv文件,但其实就是文本格式输入进去,没有csv格式

powershell 复制代码
Get-Service |
Sort-Object --Property Status, Name |
Select-Object --Property DisplayName,Status |
Out-File --FilePath ServiceList.csv

转换为其他形式的数据表示形式

PowerShell 使用两个不同的谓词进行转换: ConvertToExport

Csv

使用 ConvertTo 的命令(如 ConvertTo-Csv )接受来自管道的对象作为输入,并将转换后的数据作为输出生成到管道

切记:这个是将对象转换成了csv格式,但是你直接输入进文件可能会存在各种问题,比如ConvertTo-Csv 生成的 CSV 包含类型信息行(如 #TYPE System.ServiceProcess.ServiceController),这不是标准 CSV 的一部分,可能会干扰某些应用程序的解析,这就需要Export-Csv解决

powershell 复制代码
Get-Service | ConvertTo-Csv | Out-File Services.csv

使用 Export(如 Export-Csv)的命令执行两项作:它会转换数据,然后将数据写入外部存储,例如磁盘上的文件

powershell 复制代码
Get-Service | Export-Csv Services.csv
XML

ConvertTo-ClixmlExport-Clixml

Json

ConvertTo-Json 命令创建 JSON 格式的数据,必须使用 Out-File 或文本重定向运算符之一将 JSON 数据发送到文件

HTML

ConvertTo-Html 命令支持此功能,必须使用 Out-File 或其别名之一来定向输出。

ConvertTo-Html 创建编码为 HTML 的简单列表或表,您可以通过各种参数以有限的方式控制HTML格式,例如:

  • ‑Head。 指定 HTML 节的内容。
  • ---标题 。 设置 HTML 标题 标记的值。
  • -PreContent。 定义应在表或列表输出之前显示的任何内容。
  • -PostContent。 定义应在表或列表输出之后显示的任何内容。
其他输出选项

Out-* 命令的核心功能、常见参数和典型使用场景:

Cmdlet 核心功能 常用参数 典型应用场景
Out-Host 将输出发送到主机(控制台)进行显示 -Paging (强制分页显示) 逐页查看长输出,避免滚动过快错过信息
Out-Printer 将输出发送到打印机进行打印 -Name (指定打印机名称) 打印命令结果、报告或配置清单
Out-GridView 在交互式表格窗口中显示输出,支持排序、筛选和复制 -Title (设置窗口标题) 可视化数据分析、快速筛选和分享信息,但无法直接保存

Out-Host:控制台输出管理

  • Out-Host 是 PowerShell 默认的输出方式,即直接将结果呈现在控制台。它的特殊之处在于你可以通过 -Paging 参数手动控制输出分页
    • -Paging 参数 :强制对输出进行分页,显示一页后暂停,按空格键查看下一页,按 Q 键退出
    • more 命令的关系 :在 PowerShell 中,more 是一个内置函数,它本质上是 Out-Host -Paging 的别名,两者功能相同

逐页查看系统进程列表

powershell 复制代码
Get-Process | Out-Host -Paging

# 也可以使用更简洁的more
Get-Process | more

Out-Printer:打印输出

  • Out-Printer 允许你将命令的输出直接发送到打印机。
    • 默认行为 :不使用参数时,输出会发送到默认打印机
    • -Name 参数 :指定目标打印机的名称,打印机名称需与系统中安装的打印机名称匹配(可通过 Get-Printer cmdlet 查看所有可用打印机)

打印当前运行的进程列表到默认打印机

powershell 复制代码
Get-Process | Out-Printer

将系统服务状态发送到特定打印机(假设打印机名为 "HP-LaserJet"):

powershell 复制代码
Get-Service | Out-Printer -Name "HP-LaserJet"

打印到虚拟打印机 (如生成PDF):如果你安装了 Microsoft Print to PDF 这类虚拟打印机,也可以使用 -Name 参数指定它来生成PDF文件。

powershell 复制代码
Get-Service | Where-Object {$_.Status -eq 'Running'} | Out-Printer -Name "Microsoft Print to PDF"

Out-GridView:交互式表格输出

  • Out-GridView(通常简称 OGV )是一个非常强大的工具,它会在一个新窗口中以交互式表格的形式显示输出 。你可以:
    • 点击列名进行排序(升序/降序)。
    • 使用顶部的筛选框对任何列进行筛选(支持包含、不包含等条件)。
    • 选中行并复制(Ctrl+C),然后粘贴到 Excel 或其他应用程序中。
    • 多选 (Ctrl+点击 或 Shift+点击)。
      重要限制 :正如你所读到的,无法直接从 GridView 窗口保存数据 。你需要先通过复制粘贴,或在命令行中就使用 Export-Csv 等命令保存数据。

可视化并筛选系统服务

powershell 复制代码
# 查看所有服务
Get-Service | Out-GridView

# 仅查看正在运行的服务,并自定义窗口标题
Get-Service | Where-Object Status -eq 'Running' | Out-GridView -Title "当前运行的服务"

# 结合筛选器:找出所有正在运行且名称中包含 "windows" 的服务
Get-Service | Where-Object { $_.Status -eq 'Running' -and $_.Name -like '*windows*' } | Out-GridView

在弹出的窗口中,你可以进一步点击"状态"列排序,或在"名称"列筛选器输入更多关键字。

分析进程资源占用

powershell 复制代码
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 | Out-GridView -Title "CPU占用最高的10个进程"

自定义列顺序 :如果你使用 Select-Object 选择并排序了属性,Out-GridView 会遵循这个顺序显示列

powershell 复制代码
Get-Service | Select-Object Name, Status, DisplayName, StartType | Out-GridView

传递管道对象

管道传递数据可以有两种方式:ByValue 和 ByPropertyName

它们最根本的区别在于匹配的依据

  • ByValue :依据管道对象的整体类型进行匹配。
  • ByPropertyName :依据管道对象的属性名称 进行匹配。
    例如运行:Get-Help Stop-Process -Full
    必须看输入支持哪种类型:那我们只能输入下面的这几种了

    那既然我们要传数据给Stop-Process的话那我们就需要知道左边的那个输出符不符合这里面的输入,下面详细讲一下这个!

ByValue 传递数据


直接将String类型传给Get-Service

powershell 复制代码
'BITS','WinRM' | Get-Service

是否成功前可以看下Get-Service接受哪些类型的管道输入

powershell 复制代码
Get-Help Get-Service -Full

包含string那就可以传入了


但是这种就不行:

powershell 复制代码
Get-LocalUser | Stop-Process

首先我们看下Stop-Process的接受类型

powershell 复制代码
Get-Help Stop-Process -Full

接着再看Get-LocalUser的输出是否符合Stop-Process的输入类型

powershell 复制代码
Get-Help Get-LocalUser -Full

下图可以看到明显不符合,输出的是Object,传到Stop-Process的时候就肯定报错了

如何解决?那就交给ByPropertyName,指定输出一个属性内容即可


ByPropertyName 传递数据

这里感觉没啥好说的,就是在ByValue不成立的时候,我们就需要特别指定某个属性给到下一个要执行的命令,但是前提是左边和右边的属性名字要相同,否则在匹配的时候就不知道该属性给哪个。

(有一个特殊情况就是希望将左边的A属性值传递给右边的B属性,两个名字不一样的时候就需要重命名)


重命名方式,这种就比较特殊

  • Get-Process 有一个参数 -ComputerName,它支持通过 ByPropertyName 接收输入。
  • Get-ADComputer 返回的计算机对象有一个叫 Name 的属性,没有叫 ComputerName 的属性。
  • 因此,Name 属性无法自动传递给 -ComputerName 参数
    这样就需要重命名了:
powershell 复制代码
Get-ADComputer -Filter * | Select-Object @{Name='ComputerName'; Expression={$_.Name}} | Get-Process

若是说在 ByPropertyName 的时候,两边的属性名相同那就会自动匹配去执行操作了(这种就不详细说了)

展开属性值

先看一个动作:

powershell 复制代码
Get-Process --ComputerName (Get-LocalUser --Filter *)

这个是想要将Get-LocalUser给到Get-Process的ComputerName参数

先看Get-Process的ComputerName接受什么输入

powershell 复制代码
Get-Help Get-Process -Full 

可以看到只接受string类型,那我们直接将整个Get-LocalUser东西穿进去肯定不行

再看看这样行不行

回答:看似可以,其实也不行

powershell 复制代码
Get-Process --ComputerName (Get-LocalUser --Filter * | Select-Object --Property Name)

我们要看这个--Property Name输出的到底是不是string

powershell 复制代码
$(Get-LocalUser | Select-Object --Property Name -First 1).GetType()

查看后发现还是不是string,那这时候就需要用到展开属性的操作了

其实就是改了个参数名:

将Property改为ExpandProperty,下面这样就可以了

powershell 复制代码
$(Get-LocalUser | Select-Object --ExpandProperty Name -First 1).GetType()

为什么可以,我们直接看下取出来的Name是不是string即可


复习提问:

在命令行接口将对象从一个命令传递到管道中的另一个命令时,Windows PowerShell 总是优先尝试使用哪种技术?

powershell 复制代码
ByValue

深入了解Powershell脚本

开发生命周期与安全强化

PowerShellGet 模块

Cmdlet 说明
Find-Module 使用此 cmdlet 在 PowerShell 库中搜索 Windows PowerShell 模块。 最简单的用法是根据模块名进行搜索,但也可以根据命令名、版本、DscResource 和 RoleCapability 进行搜索。
Find-Script 使用此 cmdlet 在 PowerShell 库中搜索 Windows PowerShell 脚本。 最简单的用法是根据脚本名进行搜索,但也可以根据版本进行搜索。
PowerShell 库需要使用传输层安全性 (TLS) 1.2 来帮助保护通信。 默认情况下,Windows 10 和 Windows Server 2016 不支持在 Windows PowerShell 中使用 TLS 1.2。 因此,需要启用 TLS 1.2 才能下载 PowerShell 库内容。

若要为当前 PowerShell 提示启用 TLS 1.2,请运行以下命令:

powershell 复制代码
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

若要在计算机上永久解决此问题,需要创建注册表项。 可以运行以下两个命令来创建必要的密钥:

powershell 复制代码
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319'-Name 'SchUseStrongCrypto' -Value '1' -Type DWord

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -Value '1' -Type DWord

执行策略

确保当前配置,可以使用:Get-ExecutionPolicy

执行策略的选项包括:

  • Restricted:不允许运行任何脚本。
  • AllSigned:仅当脚本经过数字签名后才能运行。
  • RemoteSigned:下载的脚本只有在经过数字签名后才能运行。
  • Unrestricted:可以运行所有脚本,但在运行下载但未签名的脚本时会显示确认提示。
  • Bypass:运行所有脚本且不显示提示。

以数字方式对脚本进行签名

使用现有的代码签名证书(如果你有)

如果你已经从公共证书颁发机构(CA)或企业的内部CA获取并安装了代码签名证书,可以用以下命令查找:

powershell 复制代码
# 在当前用户的证书存储中查找所有可用于代码签名的证书
$certs = Get-ChildItem -Path "Cert:\CurrentUser\My" -CodeSigningCert

# 如果你确定只有一个,可以直接赋值
$cert = Get-ChildItem -Path "Cert:\CurrentUser\My" -CodeSigningCert
  • Cert:\CurrentUser\My 是 PowerShell 证书驱动器(PSDrive)中的一个路径,指向当前用户的"个人"证书存储区域。
  • -CodeSigningCert 参数是 Get-ChildItem 在证书驱动器中专用的,用于筛选出具有"代码签名"用途的证书。

创建自签名证书(用于测试和学习)

在生产环境中,你需要一个受信任的CA颁发的证书。但在测试和学习时,可以快速创建一个自签名证书

powershell 复制代码
# 以管理员身份运行 PowerShell 执行以下命令
$certParams = @{
    Type = 'CodeSigningCert'
    Subject = 'CN=PowerShell Scripting Test' # 证书主题,CN是通用名
    KeyUsage = 'DigitalSignature' # 密钥用法:数字签名
    KeyExportPolicy = 'Exportable' # 密钥可导出,方便备份和转移
    CertStoreLocation = 'Cert:\CurrentUser\My' # 证书存储位置
    HashAlgorithm = 'sha256' # 哈希算法
    # FriendlyName 是可选的,便于在证书管理中识别
    FriendlyName = 'My PowerShell Test Signing Certificate'
}
$cert = New-SelfSignedCertificate @certParams

重要提示 :自签名证书仅用于测试 。因为它不是由受信任的根证书颁发机构颁发的,所以其他计算机默认不会信任它。

(你自己创建的,你自己使用的时候就导入即可,不用设置密码啥的,因为是你自己创建你自己用,除非你导出的时候就要设置密码,因为你导出肯定是要给其他计算机使用)


从证书文件导入

如果你有 .pfx.p12 格式的证书文件(通常包含私钥),可以使用 Get-PfxCertificate cmdlet 来加载它:

powershell 复制代码
# 会弹窗提示输入密码
$cert = Get-PfxCertificate -FilePath "C:\Path\To\Your\CodeSigningCert.pfx"

# 或者使用SecureString自动输入密码(注意密码安全)
$securePassword = ConvertTo-SecureString -String "YourCertificatePassword" -Force -AsPlainText
$cert = Get-PfxCertificate -FilePath "C:\Path\To\Your\CodeSigningCert.pfx" -Password $securePassword

加载的时候需要密码,这需要在证书持有者导出证书的时候设置的那个密码,然而这个pdx或者p12文件是用来签名的,不是用来验证的,用来验证的那个是cert,这也是为啥要输入密码的原因了,这个证书是拿来公章签名的。


拿到证书对象($cert)后,就可以用它来签名脚本了。官网的例子是基础,但强烈建议添加时间戳服务器参数。

powershell 复制代码
# 基础签名(官网示例)
Set-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1" -Certificate $cert

# 推荐的签名方式(添加时间戳)
$signParams = @{
    FilePath = "C:\Scripts\MyScript.ps1" # 要签名的脚本路径
    Certificate = $cert # 之前获取的证书对象
    HashAlgorithm = 'Sha256' # 哈希算法,建议使用Sha256
    # 添加时间戳至关重要!即使证书过期,时间戳也能证明签名时证书是有效的。
    TimestampServer = 'http://timestamp.digicert.com' 
    # -IncludeChain 参数可选,默认是 'NotRoot'(包含除根CA以外的所有证书)
    # -Force 参数可选,如果脚本已有签名,强制替换
}
Set-AuthenticodeSignature @signParams

签名完成后,务必检查一下:

powershell 复制代码
Get-AuthenticodeSignature -FilePath "C:\Scripts\MyScript.ps1"

查看输出中的 Status 属性:

  • Valid:签名有效且受信任。
  • UnknownError:签名无效或证书不受信任(常见于自签名证书)。
  • NotSigned:脚本未签名。

让系统信任你的签名
!!!!!记住这里是验证,不是用来签名,前面说的都是pxf和p12证书,这里讲的是cer文件!!!!!!!!

对于自签名证书 ,由于它不是公共CA颁发的,你需要将你的自签名证书**导入到"受信任的根证书颁发机构"或"受信任的发布者"存储区。否则,在其他计算机上运行时会显示 UnknownError

  1. 将你的证书导出为 .cer 文件(只包含公钥)。
  2. 在需要运行此脚本的计算机上,将这个 .cer 文件导入到"受信任的根证书颁发机构 "或"受信任的发布者 "(对于代码签名证书,通常是"受信任的发布者")。
    你可以使用 PowerShell 自动化导入信任证书的过程
powershell 复制代码
$certPath = "C:\Path\To\Exported\Certificate.cer"
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store("TrustedPublisher", "LocalMachine")
$store.Open("ReadWrite")
$store.Add((New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath)))
$store.Close()

进阶语法

ForEach 循环

在某些情况下,可能需要使用 ForEach-Object cmdlet 来处理管道中的数据。 将数据存储在数组中时,ForEach 构造支持处理数组中的每个项。

powershell ForEach ($user in $users) { Set-ADUser $user -Department "Marketing" }

在 PowerShell 7 中,已将 -Parallel 参数添加到 ForEach-Object cmdlet。 这样,管道就可以同时处理多个对象。 相较于标准 ForEach 循环,同时处理多个对象所提供的性能更佳。 如果使用的是 PowerShell 7,应考虑这一点。 以下示例说明了如何将 ForEach-Object 与 -Parallel 参数配合使用。

powershell 复制代码
$users | ForEach-Object -Parallel { Set-ADUser $user -Department "Marketing" }

默认情况下,-Parallel 参数支持一次处理五个项,可以使用 -ThrottleLimit 参数将其修改为更大或更小的值。

If 构造

举例子:如果可用磁盘空间不足,则可以使用 If 语句显示警告

powershell 复制代码
If ($freeSpace -le 5GB) {
   Write-Host "Free disk space is less than 5 GB"
} ElseIf ($freeSpace -le 10GB) {
   Write-Host "Free disk space is less than 10 GB"
} Else {
   Write-Host "Free disk space is more than 10 GB"
}

同时也学到大小是可以直接使用MB、GB等单位直接比较

Switch 构造

powershell 复制代码
Switch ($choice) {
   1 { Write-Host "You selected menu item 1" }
   2 { Write-Host "You selected menu item 2" }
   3 { Write-Host "You selected menu item 3" }
   Default { Write-Host "You did not select a valid option" }
}

可以使用 -wildcard 参数,以与 -like 运算符相同的语法来执行模式匹配。 或者,可以使用 -regex 参数通过正则表达式执行匹配。

powershell 复制代码
Switch -WildCard ($ip) {
   "10.*" { Write-Host "This computer is on the internal network" }
   "10.1.*" { Write-Host "This computer is in London" }
   "10.2.*" { Write-Host "This computer is in Vancouver" }
   Default { Write-Host "This computer is not on the internal network" }
 }

For 构造

powershell 复制代码
For($i=1; $i -le 10; $i++) {
   Write-Host "Creating User $i"
}
复制代码
处理对象数组时,最好使用 ForEach 构造,因为在处理之前不需要计算数组中的项数。

其他循环构造

Do..While

Do..While 构造运行脚本块,直到指定条件为false

此构造保证脚本块至少运行一次

powershell 复制代码
Do {
   Write-Host "Script block to process"
} While ($answer -eq "go")

Do..Until

Do..Until 构造运行脚本块,直到指定条件为 true

此构造保证脚本块至少运行一次

powershell 复制代码
Do {
   Write-Host "Script block to process"
} Until ($answer -eq "stop")

While

While 构造运行脚本块,直到指定条件为 false

虽然它类似于 Do..While 构造,但它不能保证脚本块的运行

powershell 复制代码
While ($answer -eq "go") {
   Write-Host "Script block to process"
}

Break 和 Continue

使用 Continue 可阻止修改要修改的用户列表中的管理员用户帐户:

powershell 复制代码
ForEach ($user in $users) {
   If ($user.Name -eq "Administrator") {Continue}
   Write-Host "Modify user object"
}

Break 用于在最大帐户数已修改时结束循环:

powershell 复制代码
ForEach ($user in $users) {
   $number++
   Write-Host "Modify User object $number"
   If ($number -ge $max) {Break}
}

导入数据

Get-Content

直接读取文件内容进来

powershell 复制代码
$computers = Get-Content C:\Scripts\computers.txt

可在 Get-Content 的路径中使用通配符,以便一次获得多个文件的数据

可使用 -Include 和 -Exclude 参数修改所选文件

powershell 复制代码
Get-Content -Path "C:\Scripts\*" -Include "*.txt","*.log"

可以使用 -TotalCount 和 -Tail 参数限制使用 Get-Content 检索的数据量

  • -TotalCount 参数指定应从文件开头检索多少行
  • -Tail 参数指定从文件末尾检索多少行
    例如:
powershell 复制代码
Get-Content C:\Scripts\computers.txt -TotalCount 10

Import-Csv

powershell 复制代码
$users = Import-Csv C:\Scripts\Users.csv

输出示例:

powershell 复制代码
First,Last,UserID,Department
Amelie,Garner,AGarner,Sales
Evan,Norman,ENorman,Sales
Siu,Robben,SRobben,Sales

当我们存进一个变量后,也可以通过变量访问某个数据

powershell 复制代码
$users[2].UserID

Import-Csv 默认分隔符是逗号,有的文件不是以逗号进行分割的话你也可以使用Import-Csv,只要格式相同分隔符不同也可以用这个,前提是你要自己加参数去修改分隔符:

比如说分隔符是分号

powershell 复制代码
Import-Csv -Path .\1.csv -Header h1,h2,h3 -Delimiter ';'

Import-Clixml

powershell 复制代码
$users = Import-Clixml C:\Scripts\Users.xml

使用 -First 和 -Skip 参数来限制 Import-Clixml 检索的数据

  • -First 参数指定仅从 XML 文件的开头检索指定数量的对象
  • -Skip 参数指定从 XML 文件开头忽略指定数量的对象,并检索所有剩余的对象。

ConvertFrom-Json

powershell 复制代码
$users = Get-Content C:\Scripts\Users.json | ConvertFrom-Json

Invoke-RestMethod

Invoke-RestMethod 能够处理JSON、 XML、RSS 源和 ATOM 源。

powershell 复制代码
$users = Invoke-RestMethod "https://hr.adatum.com/api/staff"

接受用户输入

Read-Host

这种会在How many das后加上冒号然后提示用户输入:

powershell 复制代码
$answer = Read-Host "How many days"

下面这种会先打印How many days? ,-NoNewline就是不换行,然后也是等待用户输入

这种就没有冒号

powershell 复制代码
Write-Host "How many days? " -NoNewline
$answer = Read-Host

-MaskInput 或 -AsSecureString 参数在提示符处屏蔽输入用户,这种偏向于输入密码的时候不会直接显示在终端上

powershell 复制代码
$answer = Read-Host "How many days" -AsSecureString

具体使用哪个看情况了,我的电脑使用AsSecureString才行

Credential凭证使用

Get-Credential

它的核心作用就是安全地弹窗收集用户凭据(用户名和密码) ,并将其封装在一个 PSCredential 对象中,供其他需要凭据的 cmdlet 使用。

基础用法:

这会弹出一个标准对话框,让你输入用户名和密码。

powershell 复制代码
$cred = Get-Credential

高级用法(自定义提示和用户名):

  • -Message:让提示更清晰,指导用户输入什么凭据。
  • -UserName:预填用户名字段,用户只需要输入密码即可。$env:COMPUTERNAME 是环境变量,代表本机计算机名,这在工作组环境下至关重要。
powershell 复制代码
# 自定义提示信息并预填用户名
$cred = Get-Credential -Message "请输入本地管理员权限凭据" -UserName "$env:COMPUTERNAME\Administrator"

远程管理另一台工作组计算机

假设你想从计算机 CLIENT-A 远程管理计算机 CLIENT-B

powershell 复制代码
# 在 CLIENT-A 上运行
# 1. 获取 CLIENT-B 的本地管理员凭据
$cred = Get-Credential -Message "请输入CLIENT-B的本地管理员凭据" -UserName "CLIENT-B\Administrator"

# 2. 建立远程会话 (PSRemoting)
$session = New-PSSession -ComputerName "CLIENT-B" -Credential $cred

# 3. 在远程会话中执行命令(例如:检查磁盘空间)
Invoke-Command -Session $session -ScriptBlock { Get-Volume }

# 4. 关闭会话
Remove-PSSession $session

本地脚本临时提权

你用自己的标准用户账户登录,但脚本中的某些操作(如修改系统设置)需要管理员权限。

powershell 复制代码
# 检查当前用户权限,如果不是管理员则请求凭据
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Warning "此操作需要管理员权限。"
    $adminCred = Get-Credential -Message "请提供本地管理员密码以继续" -UserName "$env:COMPUTERNAME\Administrator"

    # 使用 Start-Process 以管理员身份启动一个新进程来运行命令
    $scriptBlock = {
        # 这里放需要提权的命令,例如安装Windows功能
        Enable-WindowsOptionalFeature -Online -FeatureName "Microsoft-Hyper-V" -All
    }
    Start-Process "pwsh" -ArgumentList "-Command", $scriptBlock -Credential $adminCred -Wait -NoNewWindow
} else {
    # 如果已经是管理员,直接执行命令
    Enable-WindowsOptionalFeature -Online -FeatureName "Microsoft-Hyper-V" -All
}

访问受保护的网络共享

挂载一个需要特定用户名和密码才能访问的局域网共享文件夹。

powershell 复制代码
$netCred = Get-Credential -Message "请输入访问共享\\FileServer\Data$的凭据" -UserName "FileServer\SomeUser"

# 将凭据映射到驱动器号
New-PSDrive -Name "Z" -PSProvider "FileSystem" -Root "\\FileServer\Data$" -Credential $netCred -Persist

# 现在可以像访问本地磁盘一样访问 Z:\
Get-ChildItem Z:\

# 使用完毕后断开
Remove-PSDrive -Name "Z"

Export-Clixml

首次保存凭据(在一台电脑上):

powershell 复制代码
# 弹窗输入一次凭据
$cred = Get-Credential -Message "输入要保存的凭据" -UserName "MyPC\Admin"
# 将加密后的凭据保存到文件(只能由你在本机解密)
$cred | Export-Clixml -Path "C:\Users\$env:USERNAME\Documents\secureCred.xml"

后续脚本中自动使用保存的凭据:

powershell 复制代码
# 无需弹窗,直接读取加密文件获取凭据对象
$savedCred = Import-Clixml -Path "C:\Users\$env:USERNAME\Documents\secureCred.xml"

# 使用凭据执行需要权限的操作,例如重启远程计算机
Restart-Computer -ComputerName "192.168.1.100" -Credential $savedCred -Force

脚本故障排除与错误处理

错误发生时,它们将存储在 $Error 数组中。 最近的错误始终在索引零处。 新错误生成时,会插入到 $Error[0] 处,其他错误的索引将增加一。 每当需要查看以前的错误消息时,查看 $Error 中的错误会很有帮助。 例如,如果清除屏幕,则可以通过 $Error 查看最近的错误消息。

输出命令的层次与用途

PowerShell 的输出命令不是一个简单的"打印"功能,而是一个完整的信息流系统。理解不同命令的定位是关键。

命令 用途 输出位置 是否受 *Preference 变量影响 适用场景
Write-Host 直接与用户交互 控制台 (主机) 显示进度、美观的标题、即时提示。谨慎使用
Write-Output 将对象放入输出管道 管道 / 控制台 脚本的主要输出结果。通常隐式使用(如 "Hello")。
Write-Verbose 输出详细信息 控制台 ( verbose流) 调试、记录脚本执行的详细步骤。
Write-Debug 输出调试信息 控制台 (debug流) 更深入的调试,可在运行时暂停脚本。
Write-Warning 输出警告信息 控制台 (warning流) 提示用户潜在的问题,但脚本会继续执行。
Write-Error 输出错误信息 控制台 (error流) 报告错误,但不终止脚本执行。
Throw 抛出终止错误 控制台 (error流) - 报告严重错误,并立即终止当前函数/脚本。

Write-VerboseWrite-Debug:真正的调试利器

这两个命令的强大之处在于它们的可控性 。默认情况下它们是静默的,只在需要时通过参数开启。
示例脚本 (Test-Service.ps1):

powershell 复制代码
[CmdletBinding()] # 启用高级功能,支持 -Verbose 和 -Debug 参数
param (
    [Parameter(Mandatory=$true)]
    [string]$ServiceName
)

Write-Verbose "脚本开始执行,传入的服务名参数为: $ServiceName"

# 检查服务是否存在
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
Write-Debug "Get-Service 查询结果: $($service | Out-String)"

if (-not $service) {
    Write-Error "错误: 找不到名为 '$ServiceName' 的服务。"
    exit 1
}

Write-Verbose "服务状态: $($service.Status)"
if ($service.Status -ne 'Running') {
    Write-Warning "服务 '$ServiceName' 当前未运行。"
    # 尝试启动服务
    try {
        Start-Service -Name $ServiceName
        Write-Host "服务已成功启动。" -ForegroundColor Green
    }
    catch {
        Throw "启动服务失败: $($_.Exception.Message)"
    }
} else {
    Write-Output "服务 '$ServiceName' 正在运行。"
}

Write-Verbose "脚本执行完毕。"

如何使用这个脚本:

  • 默认运行(只看到基本输出)
powershell 复制代码
.\Test-Service -ServiceName "WinRM"
# 输出: 服务 'WinRM' 正在运行。
  • 查看详细信息(使用 -Verbose
powershell 复制代码
.\Test-Service -ServiceName "WinRM" -Verbose
# 输出:
# 详细: 脚本开始执行,传入的服务名参数为: WinRM
# 详细: 服务状态: Running
# 服务 'WinRM' 正在运行。
# 详细: 脚本执行完毕。
  • 进行深度调试(使用 -Debug
powershell 复制代码
.\Test-Service -ServiceName "SomeService" -Debug

运行后会首先显示 Write-Debug 的信息,并暂停,提示你:

text 复制代码
调试: Get-Service 查询结果: 
(这里会显示Get-Service返回的详细对象信息)
继续执行?
[Y] 是(Y)  [A] 全是(A)  [N] 否(N)  [L] 全否(L)  [S] 暂停(S)  [?] 帮助 (默认值为"Y"):

按 `Y` 继续执行每一步,按 `A` 让它自动执行完所有调试步骤。这让你可以一步步观察脚本的执行流程。

使用 $VerbosePreference$DebugPreference 进行全局控制

在当前会话中开启所有Verbose输出

powershell 复制代码
$VerbosePreference = "Continue" # 默认是 "SilentlyContinue"
.\Test-Service -ServiceName "WinRM"
# 现在即使不加 -Verbose,也会输出Verbose信息

在脚本开头强制开启调试(常用于日志记录):

powershell 复制代码
[CmdletBinding()]
param()
# 在脚本内部设置,强制记录详细信息到日志文件
$VerbosePreference = "Continue"
Write-Verbose "$(Get-Date): 脚本启动..."
# ... 脚本逻辑 ...

脚本中使用断点

根据行进行断点

可使用 Set-PSBreakPoint cmdlet 设置断点

可以基于脚本行、正在使用的特定命令或正在使用的特定变量来设置断点

以下示例描述如何在脚本的特定行处设置断点:

powershell 复制代码
Set-PSBreakPoint -Script "MyScript.ps1" -Line 23

根据命令进行断点

powershell 复制代码
Set-PSBreakPoint -Command "Set-ADUser" -Script "MyScript.ps1"

基于命令设置断点时,可以包含通配符。 例如,可以使用值 *-ADUser 为 Get-ADUser、Set-ADUser、New-ADUser 和 Remove-ADUser 触发断点。

根据特定变量进行断点

powershell 复制代码
Set-PSBreakPoint -Variable "computer" -Script "MyScript.ps1" -Mode ReadWrite

可以使用变量的 -Mode 参数来确定是否要在读取和/或写入变量值时中断。 有效值为 Read、Write 和 ReadWrite。


以上是终端中运行脚本的时候设置的断点,其实我们可以用:

  • Powershell ISE 图形化工具进行根据行去断点
  • VScode 中也能够进行更多高级的断点方式

错误操作

$ErrorActionPreference

内置全局变量。 当命令生成非终止错误时,命令会检查此变量来决定该执行的操作

变量可具有下面 4 个可能值之一:

  • Continue 是默认值,它告知命令显示错误消息并继续运行。
  • SilentlyContinue 告知命令不显示错误消息,但要继续运行。
  • Inquire 告知命令显示提示,询问用户要做什么。
  • Stop 告知命令将错误视为终止错误并停止运行。

若要设置 $ErrorActionPreference 变量,请使用以下语法:

powershell 复制代码
$ErrorActionPreference = 'Inquire'
-ErrorAction

所有 Windows PowerShell 命令都有 --ErrorAction 参数。 此参数具有别名 --EA

当你使用他的时候,会覆盖$ErrorActionPreference但可以设置一样的类型,该参数仅针对当前使用的cmdlet

函数与模块

有参函数:

powershell 复制代码
Function Get-SecurityEvent {
   Param (
      [string]$ComputerName
   ) #end Param
   Get-EventLog -LogName Security -ComputerName $ComputerName -Newest 10
}

无参函数就很简单了,不写接受参数的变量即可

函数调用如下:

powershell 复制代码
Get-SecurityEvent -ComputerName LON-DC1

变量范围

使用范围修饰符 (Scope Modifiers) 指定目标作用域

这是修改其他作用域中变量的关键方法。通过在变量名前加修饰符来指定目标作用域

修饰符 作用 语法示例 说明
$global: 修改全局作用域中的变量。 $global:MyVariable = "新值" 在任何地方(函数、脚本)都能访问和修改这个全局变量。慎用,容易造成污染。
$script: 修改脚本作用域中的变量。 $script:MyVariable = "新值" 在当前脚本文件的任何函数内部修改在脚本顶层定义的变量。这是最常用、最安全的方式。
$using: 在远程命令或脚本块中引用当前局部作用域的变量。 Invoke-Command { ... -Name $using:LocalVar } 用于将本地变量的值传递到远程会话或新脚本块中,而不是在远程会话中修改它。
$private: 在当前作用域创建变量,且该变量不会传递到更高级的作用域。 $private:TempVar = "值" 用于限制变量范围,确保其不会影响父作用域。较少用。

示例:

假设您在脚本顶层定义了 $configPath,现在需要在一个函数里修改它:

Set-Variable指定作用域
powershell 复制代码
Set-Variable -Name "MyVariable" -Value "新的值" -Scope <ScopeName>

-Scope 参数值:

  • 'Global': 修改全局作用域。
  • 'Script': 修改脚本作用域。
  • 'Local': 修改当前局部作用域(默认值)。
  • 'Private': 修改为私有作用域。

绝大多数情况下,您应该使用 $script: 修饰符

这是在函数内修改脚本级变量的最标准、最可读且副作用最小的方式。

类似指针修改

使用[ref]去拿到真实的那个变量在函数里面去修改,而不是说每次修改的都是函数内部接受到的值,他会直接影响到函数外部那个传进来的变量的值

powershell 复制代码
function Modify-ByReference {
    param (
        [ref]$RefValue
    )
    $RefValue.Value = "在函数内部被修改了" # 注意:是修改 .Value 属性
}

$OriginalVariable = "原始值"
	Modify-ByReference -RefValue ([ref]$OriginalVariable)
Write-Host $OriginalVariable # 输出:在函数内部被修改了

但是我们是避免这样去修改变量的,要改的话最好是通过return返回值,然后在函数外部去改变某个变量

return

好像没啥好说的,就是return值

powershell 复制代码
function xxx{
	xxx
	return($var) # return "xxx" return $var
}

$ch = xxx()

创建模块

你必须创建与该文件同名的子文件夹,并将文件放在该子文件夹中

例如,如果你有一个名为 AdatumFunctions.psm1 的模块,

则将其放置在 C:\Program Files\WindowsPowerShell\Modules\AdatumFunctions 中。

相关推荐
jun~2 小时前
SQLMap数据库枚举靶机(打靶记录)
linux·数据库·笔记·学习·安全·web安全
wanhengidc3 小时前
本机网速会影响到云手机的运行吗
运维·服务器·安全·游戏·智能手机
伊织code4 小时前
Cybersecurity AI (CAI) - 轻量级网络安全AI框架
人工智能·安全·web安全·cybersecurity·cai
lypzcgf4 小时前
Coze源码分析-资源库-删除数据库-后端源码-安全与错误处理
数据库·安全·coze·coze源码分析·智能体平台·ai应用平台·agent平台
Gss7774 小时前
Nginx 核心安全配置总结
网络·nginx·安全
余防7 小时前
文件上传漏洞(二)iis6.0 CGI漏洞
前端·安全·web安全·网络安全
小李飞刀李寻欢8 小时前
kauditd0 病毒/挖矿程序完全清除方法初试
网络·安全·病毒·挖矿
ITHAOGE1511 小时前
下载| Windows 11 ARM版9月官方ISO系统映像 (适合部分笔记本、苹果M系列芯片电脑、树莓派和部分安卓手机平板)
arm开发·windows·科技·microsoft·微软·电脑
百事牛科技11 小时前
PPT如何退出“只读模式”?4 类场景的实用解锁方法
windows·powerpoint