我使用 ChatGPT 生成了代码,以便了解如何自行操作,而且基本上可以正常工作。该脚本用于检查我的电视和电影文件夹中是否存在缺失的文件(folder.jpg、theme.mp3 等),以便 Plex 更好地工作,并显示哪些文件缺失以及缺失的数量。
它在电影文件夹上运行得很好,但我(AI 也不能)想出如何修复它来单击电视或电视结束按钮并让它搜索这些文件夹。
我最好的猜测是第 167 行的 $pathMap 变量是关键,并且它没有正确更新此部分 - $global:fixedPath = $pathMap
有人可以纠正它并向我解释如何解决这个问题吗?
Windows 10 22H2 和 Powershell PSVersion 5.1.19041.5737
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Create form
$form = New-Object System.Windows.Forms.Form
$form.Text = "Marion's Plex checker"
$form.Size = New-Object System.Drawing.Size(700, 700)
$form.StartPosition = "CenterScreen"
# Fixed folder path
$fixedPath = "D:\ServerFolders\Videos\02 - Movies"
# Path label
$label = New-Object System.Windows.Forms.Label
$label.Text = "Checking subfolders in: $fixedPath"
$label.Location = New-Object System.Drawing.Point(120, 20)
$label.Size = New-Object System.Drawing.Size(550, 20)
$form.Controls.Add($label)
# Summary label
$summaryLabel = New-Object System.Windows.Forms.Label
$summaryLabel.Text = ""
$summaryLabel.Location = New-Object System.Drawing.Point(120, 45)
$summaryLabel.Size = New-Object System.Drawing.Size(550, 20)
$form.Controls.Add($summaryLabel)
# Output box
$logBox = New-Object System.Windows.Forms.TextBox
$logBox.Multiline = $true
$logBox.ScrollBars = "Vertical"
$logBox.Location = New-Object System.Drawing.Point(120, 75)
$logBox.Size = New-Object System.Drawing.Size(550, 350)
$logBox.Anchor = "Top,Bottom,Left,Right"
$logBox.ReadOnly = $true
$form.Controls.Add($logBox)
# Search file buttons (on the left)
$fileTypes = @("theme.mp3", "folder.jpg", "background.jpg", "poster.jpg")
$fileButtons = @{}
$yPos = 20
$global:selectedFile = "theme.mp3"
foreach ($file in $fileTypes) {
$btn = New-Object System.Windows.Forms.Button
$btn.Text = $file
$btn.Tag = $file
$btn.Location = New-Object System.Drawing.Point(10, $yPos)
$btn.Size = New-Object System.Drawing.Size(100, 30)
$form.Controls.Add($btn)
$fileButtons[$file] = $btn
$yPos += 40
$btn.Add_Click({
$global:selectedFile = $this.Tag
# Reset styles
foreach ($b in $fileButtons.Values) {
$b.UseVisualStyleBackColor = $true
}
# Highlight selected
# Reset styles
foreach ($b in $fileButtons.Values) {
$b.UseVisualStyleBackColor = $true
$b.BackColor = [System.Drawing.SystemColors]::Control
}
# Highlight selected
$this.UseVisualStyleBackColor = $false
$this.BackColor = 'LightBlue'
$summaryLabel.Text = "Selected file: $global:selectedFile"
})
}
# Scan button
$scanButton = New-Object System.Windows.Forms.Button
$scanButton.Text = "Scan"
$scanButton.Location = New-Object System.Drawing.Point(10, 440)
$scanButton.Size = New-Object System.Drawing.Size(100, 30)
$form.Controls.Add($scanButton)
# Create a new button for Movies, positioned to the right of the Scan button
$moviesButton = New-Object System.Windows.Forms.Button
$moviesButton.Text = "Movies"
$moviesButton.Size = New-Object System.Drawing.Size(100, 30)
$moviesButton.Location = New-Object System.Drawing.Point(120, 440)
# Create a new button for TV, positioned next to the Movies button
$tvButton = New-Object System.Windows.Forms.Button
$tvButton.Text = "TV"
$tvButton.Size = New-Object System.Drawing.Size(100, 30)
$tvButton.Location = New-Object System.Drawing.Point(230, 440)
# Create a new button for TV Ended, positioned next to the TV button
$tvEndedButton = New-Object System.Windows.Forms.Button
$tvEndedButton.Text = "TV Ended"
$tvEndedButton.Size = New-Object System.Drawing.Size(100, 30)
$tvEndedButton.Location = New-Object System.Drawing.Point(340, 440) # Right of the TV button
$tvEndedButton.Add_Click({
Start-Process "D:\ServerFolders\Videos\TV (Ended)" # Link to the TV Ended path
})
$form.Controls.Add($tvEndedButton)
# Path variables for Movies, TV, and TV Ended
$pathMap = @{
"Movies" = "D:\ServerFolders\Videos\02 - Movies"
"TV Current" = "D:\ServerFolders\Videos\01 - TV"
"TV Ended" = "D:\ServerFolders\Videos\TV (Ended)"
}
# Default path for Movies
$global:fixedPath = $pathMap["Movies"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Add click event for Movies button
$moviesButton.Add_Click({
$global:fixedPath = $pathMap["Movies"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$moviesButton.BackColor = 'LightGreen'
$tvButton.BackColor = [System.Drawing.SystemColors]::Control
$tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
# Start scanning the Movies path
Write-Host "Scanning Movies path: $global:fixedPath"
Scan-Folder -path $global:fixedPath
})
# Add click event for TV button
$tvButton.Add_Click({
$global:fixedPath = $pathMap["TV Current"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$tvButton.BackColor = 'LightGreen'
$moviesButton.BackColor = [System.Drawing.SystemColors]::Control
$tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
# Start scanning the TV Current path
Write-Host "Scanning TV path: $global:fixedPath"
Scan-Folder -path $global:fixedPath
})
# Add click event for TV Ended button
$tvEndedButton.Add_Click({
$global:fixedPath = $pathMap["TV Ended"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$tvEndedButton.BackColor = 'LightGreen'
$moviesButton.BackColor = [System.Drawing.SystemColors]::Control
$tvButton.BackColor = [System.Drawing.SystemColors]::Control
# Start scanning the TV Ended path
Write-Host "Scanning TV Ended path: $global:fixedPath"
Scan-Folder -path $global:fixedPath
})
# Function to scan the folder
function Scan-Folder {
param(
[string]$path
)
Write-Host "Scanning folder: $path"
# Insert the actual folder scanning logic here
}
# Path variables for Movies, TV, and TV Ended
$pathMap = @{
"Movies" = "D:\ServerFolders\Videos\02 - Movies"
"TV Current" = "D:\ServerFolders\Videos\01 - TV"
"TV Ended" = "D:\ServerFolders\Videos\TV (Ended)"
}
# Default path for Movies
$global:fixedPath = $pathMap["Movies"]
# Add click event for Movies button
$moviesButton.Add_Click({
$global:fixedPath = $pathMap["Movies"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$moviesButton.BackColor = 'LightGreen'
$tvButton.BackColor = [System.Drawing.SystemColors]::Control
$tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
# Trigger the scanning logic
Scan-Folder -path $global:fixedPath
})
# Add click event for TV button
$tvButton.Add_Click({
$global:fixedPath = $pathMap["TV Current"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$tvButton.BackColor = 'LightGreen'
$moviesButton.BackColor = [System.Drawing.SystemColors]::Control
$tvEndedButton.BackColor = [System.Drawing.SystemColors]::Control
# Trigger the scanning logic
Scan-Folder -path $global:fixedPath
})
# Add click event for TV Ended button
$tvEndedButton.Add_Click({
$global:fixedPath = $pathMap["TV Ended"]
$pathLabel.Text = "Scanning path: $global:fixedPath"
# Change button colors to indicate active path
$tvEndedButton.BackColor = 'LightGreen'
$moviesButton.BackColor = [System.Drawing.SystemColors]::Control
$tvButton.BackColor = [System.Drawing.SystemColors]::Control
# Trigger the scanning logic
Scan-Folder -path $global:fixedPath
})
# Function to scan the folder
function Scan-Folder {
param(
[string]$path
)
Write-Host "Scanning folder: $path"
# Insert the actual scanning logic here to process the folder
}
# Right of the Movies button
$tvButton.Add_Click({
Start-Process "D:\ServerFolders\Videos\01 - TV" # Link to the TV path
})
$form.Controls.Add($tvButton)
# Right of the Scan button
$moviesButton.Add_Click({
Start-Process "D:\ServerFolders\Videos\02 - Movies" # Link to the Movies path
})
$form.Controls.Add($moviesButton)
# Globals
$global:missingItems = @()
function Run-Scan {
$logBox.Clear()
$summaryLabel.Text = ""
$log = @()
$logPath = Join-Path $fixedPath "missing_${global:selectedFile}_log.txt"
$csvPath = Join-Path $fixedPath "missing_${global:selectedFile}_report.csv"
$global:missingItems = @()
if (-not (Test-Path $fixedPath)) {
[System.Windows.Forms.MessageBox]::Show("Invalid folder path.", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
return
}
$folders = Get-ChildItem -Path $fixedPath -Directory
foreach ($folder in $folders) {
$targetFile = Join-Path $folder.FullName $global:selectedFile
if (-not (Test-Path -LiteralPath $targetFile)) {
$global:missingItems += $folder
$logLine = "$($folder.Name)"
$logBox.AppendText($logLine + "`r`n")
$log += $logLine
}
}
if ($global:missingItems.Count -eq 0) {
$summaryLabel.Text = "All folders have $($global:selectedFile)."
} else {
$summaryLabel.Text = "Missing $($global:selectedFile) from [$($global:missingItems.Count)] folders."
}
$log | Out-File -FilePath $logPath -Encoding UTF8
$logBox.AppendText("`r`nLog saved to: $logPath`r`n")
$exportButton.Enabled = $global:missingItems.Count -gt 0
}
$scanButton.Add_Click({ Run-Scan })
# Export to CSV button
$exportButton = New-Object System.Windows.Forms.Button
$exportButton.Text = "Export to CSV"
$exportButton.Location = New-Object System.Drawing.Point(10, 400)
$exportButton.Size = New-Object System.Drawing.Size(100, 30)
$exportButton.Enabled = $false
$form.Controls.Add($exportButton)
$exportButton.Add_Click({
$csvPath = Join-Path $fixedPath "missing_$($global:selectedFile)_report.csv"
$csvPath = Join-Path $fixedPath "missing_$($global:selectedFile)_report.csv"
$headerLines = @(
"File type: $($global:selectedFile)",
"Total missing: $($global:missingItems.Count)`r`n"
)
$headerLines | Out-File -FilePath $csvPath -Encoding UTF8
$global:missingItems | ForEach-Object {
[PSCustomObject]@{ FolderName = $_.Name; Path = $_.FullName }
} | Export-Csv -Path $csvPath -Append -NoTypeInformation -Encoding UTF8
$logBox.AppendText("CSV exported to: $csvPath`r`n")
})
$form.Topmost = $true
$form.Add_Shown({ $form.Activate() })
[void]$form.ShowDialog()
您的问题是变量作用域之一:
您使用
$fixedPath
和$global:fixedPath
来指代您认为是同一个变量,但它们并不是:PowerShell 脚本默认在调用者的子范围内运行,这意味着脚本的范围(可以有自己的子范围,特别是在函数内部)与全局范围()不同;
$global:
您可以使用$script:
范围说明符来引用脚本的顶级范围(甚至可以从嵌入函数中引用)。虽然您可以通过仅按名称引用来读取
$fixedPath
祖先作用域中定义的变量(例如),但是当您在没有作用域说明符的情况下为它们分配值时,您会在当前作用域中隐式地使用该名称创建一个新变量,然后该新变量会遮蔽(遮蔽)祖先副本 - 除非您通过作用域说明符明确引用它。$global:fixedPath
和$fixedPath
,如果您稍后尝试仅使用 读取它,后者脚本范围副本会遮蔽全局副本$fixedPath
。换句话说:
$global:fixedPath
如果您仅使用 ,则以后对 的更新将不会被看到$fixedPath
,这就是您的问题所在。为了稳健性和概念清晰性,我建议始终使用范围说明符来引用为跨范围使用而设计的变量;但是,不要使用
$global:
,因为全局变量总是会话全局的,因此在脚本退出后仍然存在,从而污染全局命名空间。相反,使用
$script:
范围:也就是说,
$global:fixedPath
用$fixedPath
$script:fixedPath