[玩转系统] 构建 PowerShell 库存
作者:精品下载站 日期:2024-12-14 07:59:01 浏览:15 分类:玩电脑
构建 PowerShell 库存
几周前,发布了新的 Iron Scripter PowerShell 脚本挑战赛。对于这个挑战,我们被要求编写一些 PowerShell 代码,我们可以使用它们来清点我们的 PowerShell 脚本库。这是我解决这个问题的方法,这绝不是唯一的方法。
代码行数
挑战的第一部分是计算 PowerShell 脚本文件中的代码行数。具有 PowerShell 相关扩展名的任何文件。这需要几个步骤。
- 获取 PowerShell 文件
- 获取每个文件的内容
- 计算行数。
Get-ChildItem 可能是列出所有文件的命令。 Get-Content 是读取每个文件的明显选择。为了进行计数,Measure-Object 具有用于测量文件中文本行的参数。但除非您有阅读帮助文件的习惯,否则您不会知道这一点。这是一个可能的解决方案。
$Path = "C:\scripts"
Get-ChildItem -Path $Path -file -Recurse -Filter "*.ps*" |
Where-Object {$_.Extension -match "\.ps(m)?1$"} -outvariable o |
Get-Content | Measure-Object -line |
Select-Object -Property @{Name="Path";Expression={$Path}},
@{Name="TotalFiles";Expression = {$o.count}},Lines,
@{Name="Date";Expression = {Get-Date}}
运行大约需要 35 秒。我可能可以通过直接使用 .NET 类来提高性能,但我想尽可能坚持使用 cmdlet。顺便说一句,你会注意到我基本上过滤了两次。您总是希望在表达式中尽早进行过滤。我使用 -Filter 参数来获取 PowerShell 文件。这完成了大部分过滤工作。 Where-Object 使用正则表达式模式过滤掉像 foo.psx 这样的奇怪文件。
这是一个多步骤等效项。
$Path = "C:\scripts"
$files = Get-ChildItem -Path $Path -file -Recurse -Filter "*.ps*" |
Where-Object {$_.Extension -match "\.ps(m)?1$"}
$measure = $files | Get-Content | Measure-Object -line
[pscustomobject]@{
Computername = $env:COMPUTERNAME
Date = (Get-Date)
Path = $Path
TotalFiles = $files.count
TotalLines = $measure.lines
}
相同的信息以不同的方式构建。
命令库存
所以我有超过 4700 个文件。这些文件可以追溯到 2006 年。我使用过哪些命令?这是挑战的第二部分。
我最初的想法是遵循这样的过程:
- 获取所有可能的命令的列表
- 获取所有PowerShell文件
- 遍历每个文件的内容
- 使用 Select-String 将内容与命令名称匹配
这种蛮力方法是这样的:
#brute force approach
#filter out location changing functions
$cmds = (Get-Command -CommandType Cmdlet,Function).Name.where({$_ -notmatch ":|\|\.\."}) | Get-Unique
$cmds| foreach-object -Begin {$cmdHash = @{}} -process {
if (-Not $cmdhash.ContainsKey($_)) {$cmdHash.Add($_,0) }
}
$Path = "C:\scripts"
$files = (Get-ChildItem -Path $Path -file -Recurse -Filter "*.ps*").Where({$_.Extension -match "\.ps(m)?1$"})
[string[]]$keys = $cmdHash.Keys
foreach ($file in $files) {
foreach ($key in $keys) {
if (Select-String -path $file.fullname -pattern $key -Quiet) {
$cmdhash.item($key)++
}
} #foreach key
} #foreach file
($cmdhash.GetEnumerator()).WHERE({$_.value -gt 0}) | sort-object -Property Value -Descending | select-object -first 100
但这根本无法扩展,并且运行时间太长而无法实用。然后我想,为什么不使用一个巨大的正则表达式模式呢?
$r = @{}
$pester = (get-command -module pester).name
$cmds2 = $cmds.where({$pester -notcontains $_})
$pattern = $cmds2 -join "|"
[System.Text.RegularExpressions.Regex]::Matches((Get-Content $files[1].FullName),"$pattern","IgnoreCase") |
Foreach-object {
if ($r.ContainsKey($_.value)) {
$r.item($($_.value))++
}
else {
$r.add($($_.value),1)
}
}
$r.GetEnumerator() | Sort-Object Value -descending | Select-Object -first 25
这是使用上一个示例中的 $cmds。我还意识到我需要过滤掉 Pester 模块,因为像 It 和 Should 这样的命令被错误地检测到。此方法适用于 10 个文件。大约9秒就完成了。做完1000个文件大约需要2分钟。但全部 4800 个文件花了 2 个多小时。当然,我可以把跑步当成一份工作,稍后再得到结果,但我想要更好。
使用 AST
所以我转向了 AST。这就是 PowerShell 在底层处理代码的方式。我首先使用单个文件进行概念验证。
New-Variable astTokens -force
New-Variable astErr -force
$path = 'C:\scripts\JDH-Functions.ps1'
$AST = [System.Management.Automation.Language.Parser]::ParseFile($Path,[ref]$astTokens,[ref]$astErr)
$found = $AST.FindAll(
{$args[0] -is [System.Management.Automation.Language.CommandAst]},
$true
)
$asttokens 变量包含检测到的不同命令和语言元素的对象。我所需要做的就是进行一些过滤、分组和排序。
($asttokens).where({$_.tokenFlags -eq "commandname" -AND (-Not $_.nestedtokens)}) |
Select-Object -property text |
Group-Object -property text -NoElement |
Sort-Object -Property count -Descending
Function Measure-ScriptFile {
[cmdletbinding()]
[alias("msf")]
Param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[alias("fullname")]
[string]$Path
)
Begin {
Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)"
$countHash = @{}
#get all commands
#Write-Verbose "[BEGIN ] Building command inventory"
#$cmds = (Get-Command -commandtype Filter, Function, Cmdlet).Name
#Write-Verbose "[BEGIN ] Using $($cmds.count) command names"
New-Variable astTokens -force
New-Variable astErr -force
} #begin
Process {
$AST = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$astTokens, [ref]$astErr)
[void]$AST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
#filter for commands but not file system commands like notepad.exe
($asttokens).where( {$_.tokenFlags -eq "commandname" -AND (-Not $_.nestedtokens) -AND ($_.text -notmatch "\.")}) |
ForEach-Object {
#resolve alias
$token = $_
if ($_.kind -eq 'identifier') {
Try {
$value = (Get-Command -Name $_.text -ErrorAction stop).ResolvedCommandName
}
Catch {
#ignore the error
$msg = "Unresolved: text:{1} in $filename" -f $filename, $token.text
Write-Verbose $msg
$value = $null
}
} #if identifier
elseif ($token.text -eq '?') {
$Value = 'Where-Object'
}
elseif ($token.text -eq '%') {
$value = 'ForEach-Object'
}
#test if the text looks like a command name
elseif ($token.text -match "\w+-\w+") {
$value = $token.text
}
<#
Use if testing for actual commands
elseif ($cmds -contains $token.text) {
$value = $token.text
}
#>
#add the value to the counting hashtable
if ($value) {
if ($countHash.ContainsKey($value)) {
$countHash.item($value)++
}
else {
$countHash.add($value, 1)
}
} #if $value
} #foreach
} #process
End {
$countHash
Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)"
} #end
}
如果文本看起来像命令,该函数将解析别名并构建命令表。也就是说,采用动词-名词命名约定。我有代码根据 Get-Command 构建的命令名称列表测试每个可能的命令。但这至少会增加 30 秒以上的处理时间。我显然有超过 16K 个命令,这需要时间来计算。
我可以这样运行它:
$Path = "C:\scripts"
(Get-ChildItem -Path $Path -file -Recurse -Filter "*.ps*").Where({$_.Extension -match "\.ps(m)?1$"}) | Measure-ScriptFile
这还需要时间。超过 45 分钟,但绝对更快。每个文件的平均时间不到 2 秒,这还不错。
平行线
现在最大的问题是,在这种情况下,PowerShell 7 中的新并行功能是否可以发挥作用?答案是谨慎的“也许”。请记住,设置和拆除并行处理会产生开销。您需要确保您的并行操作是“具有成本效益的”。
并行处理单个文件是没有意义的。至少需要一两秒的时间来计算开销,这与我的函数处理平均文件所需的时间一样长。我的解决方案是将我的文件分成 500 个文件组,然后并行处理每个文件组。
#requires -version 7.0
[cmdletbinding()]
Param([string]$Path = "C:\scripts",[int]$ThrottleLimit = 5)
Write-Host "Getting PowerShell files from $Path" -ForegroundColor Green
$files = (Get-ChildItem -Path $Path -file -Recurse -Filter "*.ps*").Where({$_.Extension -match "\.ps(m)?1$"})
Write-Host "Processing $($files.count) files" -ForegroundColor Green
#divide the files into sets
$sets = @{}
$c=0
for ($i = 0 ; $i -lt $files.count; $i+=500) {
$c++
$start = $i
$end = $i+499
$sets.Add("Set$C",@($files[$start..$end]))
}
#process the file sets in parallel
$results = $sets.GetEnumerator() |
ForEach-Object -throttlelimit $ThrottleLimit -parallel {
. C:\scripts\Measure-ScriptFile.ps1
Write-Host "Processing $($_.name)" -fore cyan
$_.value | Measure-ScriptFile
}
#assemble the results into a single data set.
$data = @{}
foreach ($result in $results) {
$result.getenumerator().foreach({
if ($data.containskey($_.name)) {
$data.item($_.name)+=$_.value
}
else {
$data.add($_.name,$_.value)
}
})
}
#the unified results
$data
我的函数包括传递给 ForEach-Object 的 ThrottleLimit 参数。我用不同的油门值尝试了我的脚本,但它对于这项任务并没有产生太大的影响。但速度明显更快!近 5000 个文件的处理时间约为 10 分钟!
这显然是我在 PowerShell 工作中使用最多的。以下是前 25 个命令。
全部来自 2422 个命令的总体结果。
当然,这不是一个绝对的列表。它没有考虑来自我的计算机上不再存在的模块的命令或来自我可能使用过的非标准命令名称的命令,例如私有函数。但它应该足够接近。
我不认为我解决了挑战中的所有问题,但这是一个很好的起点。我将让您尝试一下代码,看看会得到什么样的结果。
随时欢迎提出意见和问题。
更新
查看此代码的扩展版本,现已打包为 PowerShell 模块,网址为 https://jdhisolutions.com/blog/powershell/7559/an-expanded-powershell-scripting-inventory-tool/
猜你还喜欢
- 03-30 [玩转系统] 如何用批处理实现关机,注销,重启和锁定计算机
- 02-14 [系统故障] Win10下报错:该文件没有与之关联的应用来执行该操作
- 01-07 [系统问题] Win10--解决锁屏后会断网的问题
- 01-02 [系统技巧] Windows系统如何关闭防火墙保姆式教程,超详细
- 12-15 [玩转系统] 如何在 Windows 10 和 11 上允许多个 RDP 会话
- 12-15 [玩转系统] 查找 Exchange/Microsoft 365 中不活动(未使用)的通讯组列表
- 12-15 [玩转系统] 如何在 Windows 上安装远程服务器管理工具 (RSAT)
- 12-15 [玩转系统] 如何在 Windows 上重置组策略设置
- 12-15 [玩转系统] 如何获取计算机上的本地管理员列表?
- 12-15 [玩转系统] 在 Visual Studio Code 中连接到 MS SQL Server 数据库
- 12-15 [玩转系统] 如何降级 Windows Server 版本或许可证
- 12-15 [玩转系统] 如何允许非管理员用户在 Windows 中启动/停止服务
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[影视] 黑道中人 Alto Knights(2025)剧情 犯罪 历史 电影
[古装剧] [七侠五义][全75集][WEB-MP4/76G][国语无字][1080P][焦恩俊经典]
[实用软件] 虚拟手机号 电话 验证码 注册
[电视剧] 安眠书店/你 第五季 You Season 5 (2025) 【全10集】
[电视剧] 棋士(2025) 4K 1080P【全22集】悬疑 犯罪 王宝强 陈明昊
[软件合集] 25年6月5日 精选软件22个
[软件合集] 25年6月4日 精选软件36个
[短剧] 2025年06月04日 精选+付费短剧推荐33部
[短剧] 2025年06月03日 精选+付费短剧推荐25部
[软件合集] 25年6月3日 精选软件44个
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电视剧] 欢乐颂.5部全 (2016-2024)
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[影视] 【稀有资源】香港老片 艺坛照妖镜之96应召名册 (1996)
[剧集] 神经风云(2023)(完结).4K
[剧集] [BT] [TVB] [黑夜彩虹(2003)] [全21集] [粤语中字] [TV-RMVB]
[实用软件] 虚拟手机号 电话 验证码 注册
[资源] B站充电视频合集,包含多位重量级up主,全是大佬真金白银买来的~【99GB】
[影视] 内地绝版高清录像带 [mpg]
[书籍] 古今奇书禁书三教九流资料大合集 猎奇必备珍藏资源PDF版 1.14G
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电影] 美国队长4 4K原盘REMUX 杜比视界 内封简繁英双语字幕 49G
[电影] 死神来了(1-6)大合集!
[软件合集] 25年05月13日 精选软件16个
[精品软件] 25年05月15日 精选软件18个
[绝版资源] 南与北 第1-2季 合集 North and South (1985) /美国/豆瓣: 8.8[1080P][中文字幕]
[软件] 25年05月14日 精选软件57个
[短剧] 2025年05月14日 精选+付费短剧推荐39部
[短剧] 2025年05月15日 精选+付费短剧推荐36部
- 最新评论
-
- 热门tag