添加Write-Log函数后PowerShell文件移动脚本失效问题求助
问题:添加Write-Log函数后PowerShell文件移动脚本失效
我写了一个PowerShell脚本,用来监控文件夹并将文件移动到对应目标文件夹。移除Write-Log函数时,脚本可以正常将文件从源文件夹移动到三个目标文件夹;但添加该函数后,脚本仅能完成日志记录,无法执行文件移动操作。以下是我的脚本:
$folder = 'C:\test' $filter = '*.*' $directory_source = $directory_source = 'C:\test1\*' # <-- set this according to your requirements $directory_target_cop = 'C:\Folder1' $directory_target_prop = 'C:\Folder2' $directory_target_dom = 'C:\Folder3' $LogTime = Get-Date -Format "MM-dd-yyyy" $dtNow = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" $script:loggingpathpreference = 'C:\PSLogs\'+$LogTime+".log" Set-PSBreakpoint -Variable Now -Mode Read -Action {Set-Variable Now (get-date -uformat '%Y\%m\%d %H:%M:%S') -Option ReadOnly, AllScope -Scope Global -Force -ErrorAction SilentlyContinue} -ErrorAction SilentlyContinue $fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property @{ IncludeSubdirectories = $true # <-- set this according to your requirements NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite' } Function Write-Log { [cmdletbinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] $Message, [switch]$Warning ) Process{ if($Message -isnot [string]){ $Message = $Message | Out-String } Write-Verbose $message Add-Content -Path $script:LoggingPathPreference -Value "`n$($dtNow) : $($Message)" if($Warning){ Write-Warning $Message } } } $onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action { $path = $Event.SourceEventArgs.FullPath $name = $Event.SourceEventArgs.Name $changeType = $Event.SourceEventArgs.ChangeType $timeStamp = $Event.TimeGenerated Write-Log "The file '$name' was $changeType at $timeStamp" try{ $files = $(ls $directory_source) foreach ($file in $files) { if ( $(gc $file | Select-String "PROP" -Quiet) ) { mv -Path $file.Fullname -Destination $directory_target_prop } elseif ( $(gc $file | Select-String "COP" -Quiet) ) { mv -Path $file.Fullname -Destination $directory_target_cop } elseif ( $(gc $file | Select-String "DOM" -Quiet) ) { mv -Path $file.Fullname -Destination $directory_target_dom } } }catch{ $ErrorMessage = $_.Exception.Message Write-Log $ErrorMessage } }
问题根源
Register-ObjectEvent的-Action脚本块运行在独立的PowerShell运行空间中,默认无法访问主脚本里定义的变量(比如$directory_source、$directory_target_*)和Write-Log函数。当Action块尝试调用Write-Log或访问未定义的变量时,会抛出静默异常,导致后续的文件移动逻辑根本没有执行;而catch块也因为同样的问题无法记录错误,看起来就像脚本只完成了日志操作(实际初始的日志调用也可能未成功)。
另外还有两个小问题:
$directory_source存在重复赋值的冗余代码;$dtNow是脚本启动时的固定时间,所有日志都会使用这个时间,而非日志生成的实时时间。
解决方案
我们需要让Action块能访问主脚本的变量和Write-Log函数,同时修复日志时间等细节问题,修改后的脚本如下:
$folder = 'C:\test' $filter = '*.*' $directory_source = 'C:\test1\*' # <-- 修正重复赋值 $directory_target_cop = 'C:\Folder1' $directory_target_prop = 'C:\Folder2' $directory_target_dom = 'C:\Folder3' $LogTime = Get-Date -Format "MM-dd-yyyy" $script:loggingpathpreference = "C:\PSLogs\$LogTime.log" # 移除与核心逻辑无关的断点(若需要可保留) # Set-PSBreakpoint -Variable Now -Mode Read -Action {Set-Variable Now (get-date -uformat '%Y\%m\%d %H:%M:%S') -Option ReadOnly, AllScope -Scope Global -Force -ErrorAction SilentlyContinue} -ErrorAction SilentlyContinue $fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property @{ IncludeSubdirectories = $true # <-- set this according to your requirements NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite' } Function Write-Log { [cmdletbinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] $Message, [switch]$Warning ) Process{ if($Message -isnot [string]){ $Message = $Message | Out-String } $currentTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" # 使用实时时间记录日志 Write-Verbose $message # 确保日志目录存在,避免写入失败 if(-not (Test-Path (Split-Path $script:LoggingPathPreference -Parent))){ New-Item (Split-Path $script:LoggingPathPreference -Parent) -ItemType Directory | Out-Null } Add-Content -Path $script:LoggingPathPreference -Value "`n$currentTime : $Message" if($Warning){ Write-Warning $Message } } } $onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action { $path = $Event.SourceEventArgs.FullPath $name = $Event.SourceEventArgs.Name $changeType = $Event.SourceEventArgs.ChangeType $timeStamp = $Event.TimeGenerated # 使用Using:前缀引用主脚本的函数和变量 $writeLog = $Using:Write-Log $sourceDir = $Using:directory_source $targetCop = $Using:directory_target_cop $targetProp = $Using:directory_target_prop $targetDom = $Using:directory_target_dom # 调用日志函数 & $writeLog "The file '$name' was $changeType at $timeStamp" try{ $files = Get-ChildItem -Path $sourceDir foreach ($file in $files) { # 增强错误处理,避免无法读取的文件中断脚本 $content = Get-Content -Path $file.FullName -ErrorAction Stop if ($content | Select-String "PROP" -Quiet) { Move-Item -Path $file.FullName -Destination $targetProp -Force & $writeLog "Moved file '$($file.Name)' to $targetProp" } elseif ($content | Select-String "COP" -Quiet) { Move-Item -Path $file.FullName -Destination $targetCop -Force & $writeLog "Moved file '$($file.Name)' to $targetCop" } elseif ($content | Select-String "DOM" -Quiet) { Move-Item -Path $file.FullName -Destination $targetDom -Force & $writeLog "Moved file '$($file.Name)' to $targetDom" } } }catch{ $ErrorMessage = $_.Exception.Message & $writeLog -Warning "Error occurred: $ErrorMessage" } }
修改说明
- 跨运行空间访问:通过
$Using:前缀,让Action块可以访问主脚本中的变量和函数,解决了核心的访问权限问题; - 实时日志时间:将固定的
$dtNow改为每次调用Write-Log时获取当前时间,保证日志时间的准确性; - 目录检查:添加日志目录存在性检查,避免因目录不存在导致日志写入失败;
- 错误处理增强:读取文件时添加
-ErrorAction Stop确保异常能被捕获,移动文件时添加-Force支持覆盖同名文件(可根据需求调整); - 代码清理:移除冗余的变量赋值和无关断点,让脚本更简洁。
内容的提问来源于stack exchange,提问作者Roy
相关产品推荐
相关产品推荐

