[玩转系统] 将 PowerShell 脚本转换为函数
作者:精品下载站 日期:2024-12-14 08:04:55 浏览:13 分类:玩电脑
将 PowerShell 脚本转换为函数
最近,我分享了一些将函数导出到文件的 PowerShell 代码。这是一个很受欢迎的帖子。当我的朋友理查德·希克斯(Richard Hicks)(没有亲属关系)询问如何将文件转换为函数时,他以为我们在开玩笑。他的想法是采用一堆 PowerShell 脚本,将它们转换成一组函数,然后将它们组织成一个模块。这并不是那么牵强。所以我最近几天花了一些时间想出了一个 PowerShell 函数来获取现有的 PowerShell 脚本文件并将其转换为 PowerShell 函数。
实际上,在函数内运行代码和在脚本内运行代码没有区别。函数的核心是一个脚本块,其名称使其更易于运行。脚本块内的代码与独立脚本中的代码没有什么不同。根据您的脚本,您可以简单地将脚本代码包装在函数声明中。当然,最佳实践是让函数执行单个任务并将单个类型的对象写入管道,因此您可能仍然需要编辑生成的函数。我想出了一个使用 AST 来加速这一过程的 PowerShell 工具。
基本示例
这是一个非常简单的 PowerShell 脚本。
#requires -version 3.0
#this is a sample script
Write-Host "this is a sample script that doesn't do anything but write a random number" -ForegroundColor Yellow
Get-Random -Minimum 1 -Maximum 1000
我想将其转换为 PowerShell 函数,因此我将使用我的新工具。
我的 Convert-ScriptToFunction 命令显然需要文件的路径。我还需要为新函数指定一个名称。转换生成一个新的函数定义,并带有基于注释的帮助。原始脚本没有定义参数,因此转换定义了它们。如果我想将其保存到文件中,我需要做的就是运行命令并通过管道传输到 Out-File。然后我可以在脚本编辑器中打开该文件来完善它。
获取需求
您会注意到新代码包含原始文件中的 #requires 语句。我编写了一个单独的函数,也使用 AST 来获取任何需求。在我的 New-SystemReport.ps1 脚本中,我有这些要求。
#requires -version 5.1
#requires -module CimCmdlets
我可以使用这个功能来发现它们。
Function Get-PSRequirements {
[cmdletbinding()]
Param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[string]$Path
)
Begin {
Write-Verbose "Starting $($MyInvocation.MyCommand)"
New-Variable astTokens -Force
New-Variable astErr -Force
}
Process {
$Path = Convert-Path $path
Write-Verbose "Processing $path"
$AST = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$astTokens, [ref]$astErr)
#add the Path as a property
if ($ast.ScriptRequirements) {
$ast.ScriptRequirements | Add-Member -MemberType NoteProperty -Name "Path" -Value $Path-Force -PassThru
}
else {
Write-Verbose "No requirements detected in $Path."
}
}
End {
Write-Verbose "Ending $($MyInvocation.MyCommand)"
}
}
基于评论的帮助
我还希望能够生成基于注释的帮助(如果尚未定义)。我使用 AST ParamBlock 创建了这样一个块。我总是告诉人们使用 HelpMessage 属性,如果你这样做,它将用于 .Parameter 定义。
Function New-CommentHelp {
[cmdletbinding()]
[OutputType("string")]
Param([System.Management.Automation.Language.ParamBlockAst]$ParamBlock)
$h = [System.Collections.Generic.List[string]]::new()
$h.Add("<#")
$h.Add("`t.Synopsis")
$h.Add("`t <short description>")
$h.add("`t.Description")
$h.add("`t <long description>")
foreach ($p in $ParamBlock.Parameters) {
$paramName = $p.name.variablepath.userpath
$h.Add("`t.Parameter $paramName")
$paramHelp = $p.Attributes.namedArguments.where({ $_.argumentname -eq 'helpmessage' })
if ($paramHelp) {
$h.add("`t $($paramHelp.argument.value)")
}
else {
$h.Add("`t <enter a parameter description>")
}
}
$h.add("`t.Example")
$h.Add("`t PS C:\> $Name")
$h.Add("`t <output and explanation>")
$h.Add("`t.Link")
$h.Add("`t <enter a link reference>")
$h.Add("#>")
$h
}
这是一个示例脚本。
#requires -version 3.0
#this is a sample script
Param (
[Parameter(Position = 0,HelpMessage = "How many numbers do you want?")]
[int]$Count = 1,
[string]$Name,
[switch]$Demo
)
Write-Host "this is a sample script that doesn't do anything but write a random number" -ForegroundColor Yellow
#get numbers
1..$count | Foreach-Object {
Get-Random -Minimum 1 -Maximum 1000
}
Write-Host "Ending script" -ForegroundColor yellow
#eof
使用我的转换函数我得到这个输出。
#requires -version 3.0
# Function exported from C:\scripts\SampleScript3.ps1
Function Invoke-Sample {
<#
.Synopsis
<short description>
.Description
<long description>
.Parameter Count
How many numbers do you want?
.Parameter Name
<enter a parameter description>
.Parameter Demo
<enter a parameter description>
.Example
PS C:\> Invoke-Sample
<output and explanation>
.Link
<enter a link reference>
#>
[cmdletbinding()]
Param (
[Parameter(Position = 0,HelpMessage = "How many numbers do you want?")]
[int]$Count = 1,
[string]$Name,
[switch]$Demo
)
Write-Host "this is a sample script that doesn't do anything but write a random number" -ForegroundColor Yellow
1..$count | Foreach-Object {
Get-Random -Minimum 1 -Maximum 1000
}
Write-Host "Ending script" -ForegroundColor yellow
} #close Invoke-Sample
函数名称
因为我必须定义一个新的函数名称,所以我希望它尽可能简单。我使用这个辅助函数将名称格式化为正确的大小写。我假设采用动词-名词命名约定。
Function Format-FunctionName {
[cmdletbinding()]
Param (
[ValidateScript({
if ($_ -match "^\w+-\w+$") {
$true
}
else {
Throw "Your function name should have a Verb-Noun naming convention"
$False
}
})]
[string]$Name
)
$split = $name -split "-"
"{0}{1}-{2}{3}" -f $split[0][0].ToString().ToUpper(), $split[0].Substring(1).Tolower(),$split[1][0].ToString().ToUpper(), $split[1].Substring(1).ToLower()
}
它并不总是完美的,特别是当你的名词像这个例子一样复杂时。该函数也不会验证您是否使用标准动词。但是,我有一个参数完成器,它将插入一个标准动词。
Register-ArgumentCompleter -CommandName Convert-ScriptToFunction -ParameterName Name -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
#PowerShell code to populate $wordtoComplete
Get-Verb | Where-Object {$_.verb -match "^$wordToComplete"} |
ForEach-Object {
#this will autocomplete with Verb-
[System.Management.Automation.CompletionResult]::new("$($_.verb)-", $_.verb, 'ParameterValue', $_.Group)
}
}
我需要做的就是开始输入动词名称并按 Tab 键。补全将插入动词和破折号。我需要输入的只是名词。
使用输出
我的函数完全按照它所说的那样执行。它转换。由您决定如何使用输出。您可以通过管道传输到 Out-File 或 Set-Clipboard。但由于我预计您将立即编辑输出,因此我添加了一个名为 ToEditor 的动态参数(使用我的 New-PSDynamicParameterForm 函数)。如果您在 PowerShell ISE 或 VS Code 中运行该函数,则会定义此参数。输出将在一个新的、未保存的文件中打开。
同样,这是源文件。这些是我创建的脚本文件只是为了测试我的功能。
#requires -version 4.0
#requires -runasAdministrator
#this is a sample script
Param (
[Parameter(Position = 0,HelpMessage = "How many numbers do you want?")]
[ValidateRange(1,100)]
[int]$Count = 1
)
DynamicParam {
#this is a sample dynamic parameter
If ($True) {
$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
# Defining parameter attributes
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = '__AllParameterSets'
$attributeCollection.Add($attributes)
# Defining the runtime parameter
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('Demo', [String], $attributeCollection)
$paramDictionary.Add('Demo', $dynParam1)
return $paramDictionary
} # end if
} #end DynamicParam
Begin {
Write-Host "this is a sample script that doesn't do anything but write a random number" -ForegroundColor Yellow
}
Process {
#get numbers
1..$count | Foreach-Object {
Get-Random -Minimum 1 -Maximum 1000
}
}
End {
write-host "Ending script" -ForegroundColor yellow
}
#eof
我将在 VS Code 的集成 PowerShell 终端中运行此代码以创建一个新文件。该函数还将获得一个新的别名。
Convert-ScriptToFunction .\SampleScript4.ps1 -Name Invoke-Sample -Alias ins -ToEditor
然后,我可以使用 VSCode 清理文件,例如格式化、扩展别名以及将制表符转换为空格。然后我就可以保存文件了。输出包括对原始源文件的引用。
将脚本转换为函数
这是转换函数。
Function Convert-ScriptToFunction {
<#
.Synopsis
Convert a script file to a PowerShell function.
.Description
This command takes the body of a script file and wraps it in a function
declaration. The command will insert missing elements like [cmdletbinding()]
and comment-based help. You will most likely need to edit and clean up the
result in your scripting editor.
If you run this command in the PowerShell ISE or the VS Code PowerShell
integrated terminal, you can use the dynamic parameter ToEditor to open a
new file with with the output. You can edit and save the file manually.
It is assumed that your script file is complete and without syntax errors.
.Parameter Path
Enter the path to your PowerShell script file.
.Parameter Name
What is the name of your new function? It should have a Verb-Noun name.
.Parameter Alias
Define an optional alias for your new function.
.Parameter ToEditor
If you run this command in the PowerShell ISE or the VS Code PowerShell
integrated terminal, you can use this dynamic parameter to open a new
file with with the output. You can edit and save the file manually.
.Example
PS C:\> Convert-ScriptToFunction c:\scripts\Daily.ps1 -name Invoke-DailyTask | Set-Clipboard
Convert Daily.ps1 to a function called Invoke-DailyTask and copy the
results to the Windows clipboard. You can then paste the results into
scripting editor.
.Example
PS C:\> Convert-ScriptToFunction c:\scripts\systemreport.ps1 -name New-SystemReport | Out-File c:\scripts\New-SystemReport.ps1
Convert the SystemReport.ps1 script file to a function called
New-SystemReport and save the results to a file.
.Example
PS C:\> Convert-ScriptToFunction c:\scripts\systemreport.ps1 -name New-System -alias nsr | Tee-Object -variable f
Convert the script to a function called New-System and tee the output to $f.
This will also define an function alias of nsr.
#>
[cmdletbinding()]
[Outputtype("System.String")]
[alias('csf')]
Param(
[Parameter(
Position = 0,
Mandatory,
ValueFromPipelineByPropertyName,
HelpMessage = "Enter the path to your PowerShell script file."
)]
[ValidateScript({Test-Path $_ })]
[ValidatePattern("\.ps1$")]
[string]$Path,
[Parameter(
Position = 1,
Mandatory,
ValueFromPipelineByPropertyName,
HelpMessage = "What is the name of your new function?")]
[ValidateScript({
if ($_ -match "^\w+-\w+$") {
$true
}
else {
Throw "Your function name should have a Verb-Noun naming convention"
$False
}
})]
[string]$Name,
[Parameter(ValueFromPipelineByPropertyName,HelpMessage = "Specify an optional alias for your new function. You can define multiple aliases separated by commas.")]
[ValidateNotNullOrEmpty()]
[string[]]$Alias
)
DynamicParam {
<#
If running this function in the PowerShell ISE or VS Code,
define a ToEditor switch parameter
#>
If ($host.name -match "ISE|Code") {
$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
# Defining parameter attributes
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributes = New-Object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = '__AllParameterSets'
$attributeCollection.Add($attributes)
# Defining the runtime parameter
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter('ToEditor', [Switch], $attributeCollection)
$paramDictionary.Add('ToEditor', $dynParam1)
return $paramDictionary
} # end if
} #end DynamicParam
Begin {
Write-Verbose "Starting $($MyInvocation.MyCommand)"
Write-Verbose "Initializing"
New-Variable astTokens -Force
New-Variable astErr -Force
$new = [System.Collections.Generic.list[string]]::new()
} #begin
Process {
#normalize
$Path = Convert-Path $path
$Name = Format-FunctionName $Name
Write-Verbose "Processing $path"
$AST = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$astTokens, [ref]$astErr)
if ($ast.extent) {
Write-Verbose "Getting any comment based help"
$ch = $astTokens | Where-Object { $_.kind -eq 'comment' -AND $_.text -match '\.synopsis' }
if ($ast.ScriptRequirements) {
Write-Verbose "Adding script requirements"
if($ast.ScriptRequirements.RequiredPSVersion) {
$new.Add("#requires -version $($ast.ScriptRequirements.RequiredPSVersion.ToString())")
}
if ($ast.ScriptRequirements.RequiredModules) {
Foreach ($m in $ast.ScriptRequirements.RequiredModules) {
#test for version requirements
$ver = $m.psobject.properties.where({$_.name -match 'version' -AND $_.value})
if ($ver) {
$new.Add("#requires -module @{ModuleName = '$($m.name)';$($ver.Name) = '$($ver.value)'}")
}
else {
$new.add("#requires -module $($m.Name)")
}
}
}
if ($ast.ScriptRequirements.IsElevationRequired) {
$new.Add("#requires -RunAsAdministrator")
}
If ($ast.ScriptRequirements.requiredPSEditions) {
$new.add("#requires -PSEdition $($ast.ScriptRequirements.requiredPSEditions)")
}
$new.Add("`n")
}
else {
Write-Verbose "No script requirements found"
}
$head = @"
# Function exported from $Path
Function $Name {
"@
$new.add($head)
if ($ch) {
$new.Add($ch.text)
$new.Add("`n")
}
else {
Write-Verbose "Generating new comment based help from parameters"
New-CommentHelp -ParamBlock $ast.ParamBlock | Foreach-Object { $new.Add("$_")}
$new.Add("`n")
}
[regex]$rx = "\[cmdletbinding\(.*\)\]"
if ($rx.Ismatch($ast.Extent.text)) {
Write-Verbose "Using existing cmdletbinding"
#use the first match
$cb = $rx.match($ast.extent.text).Value
$new.Add("`t$cb")
}
else {
Write-Verbose "Adding [cmdletbinding()]"
$new.Add("`t[cmdletbinding()]")
}
if ($alias) {
Write-Verbose "Adding function alias definition $($alias -join ',')"
$new.Add("`t[Alias('$($alias -join "','")')]")
}
if ($ast.ParamBlock) {
Write-Verbose "Adding defined Param() block"
[void]($ast.ParamBlock.tostring().split("`n").Foreach({$new.add("`t$_")}) -join "`n")
$new.Add("`n")
}
else {
Write-Verbose "Adding Param() block"
$new.add("`tParam()")
}
if ($ast.DynamicParamBlock) {
#assumes no more than 1 dynamic parameter
Write-Verbose "Adding dynamic parameters"
[void]($ast.DynamicParamBlock.tostring().split("`n").Foreach({$new.Add($_)}) -join "`n")
}
if ($ast.BeginBlock.Extent.text) {
Write-Verbose "Adding defined Begin block"
[void]($ast.BeginBlock.Extent.toString().split("`n").Foreach({$new.Add($_)}) -join "`n")
$UseBPE = $True
}
if ($ast.ProcessBlock.Extent.text) {
Write-Verbose "Adding defined Process block"
[void]($ast.ProcessBlock.Extent.ToString().split("`n").Foreach({$new.add($_) }) -join "`n")
}
if ($ast.EndBlock.Extent.text) {
if ($UseBPE) {
Write-Verbose "Adding opening End{} block"
$new.Add("`tEnd {")
}
Write-Verbose "Adding the remaining code or defined endblock"
[void]($ast.Endblock.Statements.foreach({ $_.tostring() }).Foreach({ $new.Add($_)}))
if ($UseBPE) {
Write-Verbose "Adding closing End {} block"
$new.Add("`t}")
}
}
else {
$new.Add("End { }")
}
Write-Verbose "Closing the function"
$new.Add( "`n} #close $name")
if ($PSBoundParameters.ContainsKey("ToEditor")) {
Write-Verbose "Opening result in editor"
if ($host.name -match "ISE") {
$newfile = $psise.CurrentPowerShellTab.Files.add()
$newfile.Editor.InsertText(($new -join "`n"))
$newfile.editor.select(1,1,1,1)
}
elseif ($host.name -match "Code") {
$pseditor.Workspace.NewFile()
$ctx = $pseditor.GetEditorContext()
$ctx.CurrentFile.InsertText($new -join "`n")
}
else {
$new -join "`n" | Set-Clipboard
Write-Warning "Can't detect the PowerShell ISE or VS Code. Output has been copied to the clipboard."
}
}
else {
Write-Verbose "Writing output [$($new.count) lines] to the pipeline"
$new -join "`n"
}
} #if ast found
else {
Write-Warning "Failed to find a script body to convert to a function."
}
} #process
End {
Write-Verbose "Ending $($MyInvocation.mycommand)"
}
}
该函数假设您的脚本文件在语法上是完整的并且没有错误。了解并非每个脚本文件都可以转换为可以立即使用的 PowerShell 函数。转换功能正在尽最大努力。您应该将其视为加速脚本编写工作的工具。我想我需要编辑新文件。但这是一个好的开始。
我可以使用一个旧的 PowerShell 脚本,如下所示:
#requires -version 3.0
#Basic-HotFixReport.ps1
Param([string[]]$Computername = $env:COMPUTERNAME)
$ErrorActionPreference = "SilentlyContinue"
Get-Hotfix -ComputerName $Computername |
Select-Object -Property PSComputername,HotFixID,Description,InstalledBy,InstalledOn,
@{Name="Online";Expression={$_.Caption}}
并创建一个新函数:
csf .\Basic-HotfixReport.ps1 -Name Get-HotFixReport -Alias ghfr | out-file c:\scripts\get-hotfixreport.ps1
我的函数有一个别名 csf。编辑文件使其更新后,我有这样的东西。
#requires -version 5.1
# Function exported from C:\scripts\Basic-HotfixReport.ps1
Function Get-HotfixReport {
<#
.Synopsis
Get a hotfix report
.Description
Use this command to get a report of installed hotfixes on a computer.
.Parameter Computername
Enter the name of a computer.
.Example
PS C:\scripts> Get-HotfixReport thinkp1 | format-table
Computername HotFixID Description InstalledBy InstalledOn Online
------------ -------- ----------- ----------- ----------- ------
THINKP1 KB5006363 Update NT AUTHORITY\SYSTEM 11/6/2021 12:00:00 AM http://support.microsoft.com/?kbid=5006363
THINKP1 KB5004567 Update NT AUTHORITY\SYSTEM 7/4/2021 12:00:00 AM https://support.microsoft.com/help/5004567
THINKP1 KB5008295 Update NT AUTHORITY\SYSTEM 11/6/2021 12:00:00 AM https://support.microsoft.com/help/5008295
THINKP1 KB5007262 Update NT AUTHORITY\SYSTEM 11/22/2021 12:00:00 AM https://support.microsoft.com/help/5007262
THINKP1 KB5007414 Update NT AUTHORITY\SYSTEM 11/13/2021 12:00:00 AM
.Link
Get-HotFix
#>
[cmdletbinding()]
[Alias('ghfr')]
Param([string[]]$Computername = $env:COMPUTERNAME)
Try {
Get-HotFix -ComputerName $Computername -ErrorAction Stop |
Select-Object -Property @{Name = "Computername"; Expression = { $_.CSName } },
HotFixID, Description, InstalledBy, InstalledOn,
@{Name = "Online"; Expression = { $_.Caption } }
}
Catch {
Throw $_
}
} #close Get-Hotfixreport
我可能会继续完善该功能。
构建模块
我将用概念验证来总结这一点。假设我知道要使用的脚本文件和要分配的函数名称,我可以使用 PowerShell 脚本快速构建模块。
#a proof of concept to convert scripts to a new module
#dot source the conversion functions
. C:\scripts\dev-scripttofunction.ps1
$NewModuleName = "PSMagic"
$Description = "A sample module"
$ParentPath = "C:\work"
$path = New-Item -Name $NewModuleName -Path $ParentPath -ItemType Directory -Force
#create the module structure
"docs", "functions", "en-us", "formats" |
ForEach-Object { New-Item -Path $path -Name $_ -ItemType Directory }
#file data
$data = @"
"Path","Name"
"C:\scripts\SampleScript.ps1","Get-Foo"
"C:\scripts\SampleScript2.ps1","Set-Foo"
"C:\scripts\SampleScript3.ps1","Invoke-Foo"
"C:\scripts\SampleScript4.ps1","Remove-Foo"
"C:\scripts\SampleScript5.ps1","Test-Foo"
"@
$csv = $data | ConvertFrom-Csv
foreach ($item in $csv) {
$out = Join-Path $path\functions "$($item.name).ps1"
$item | Convert-ScriptToFunction | Out-File -FilePath $out
Get-Item $out
} #foreach item
#create the root module
$psm1 = @"
Get-Childitem `$psscriptroot\functions\*.ps1 |
Foreach-Object {
. `$_.FullName
}
"@
$psm1 | Out-File "$path$newmodulename.psm1"
#create the module manifest
$splat = @{
Path = "$path$newmodulename.psd1"
RootModule = "$path$newmodulename.psm1"
ModuleVersion = "0.1.0"
Author = "Jeff Hicks"
Description = $Description
FunctionsToExport = $csv.name
PowerShellVersion = "5.1"
CompatiblePSEditions = "Desktop"
}
New-ModuleManifest @splat
Get-ChildItem $path
运行这个脚本可以快速构建我的模块。
当然,仍然会有编辑和修改,但这给了我在这个过程中一个巨大的跳跃。
下一步
我希望你们中的一些人能够尝试一下这段代码,并让我知道您的想法。请记住,它可能不会生成完美的 PowerShell 函数。我想我现在有足够的命令,可以将所有这些命令捆绑到一个新模块中。事实上,我可以使用这些工具本身来构建模块。谈论元!
猜你还喜欢
- 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