You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加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块也因为同样的问题无法记录错误,看起来就像脚本只完成了日志操作(实际初始的日志调用也可能未成功)。

另外还有两个小问题:

  1. $directory_source存在重复赋值的冗余代码;
  2. $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"
    }
}

修改说明

  1. 跨运行空间访问:通过$Using:前缀,让Action块可以访问主脚本中的变量和函数,解决了核心的访问权限问题;
  2. 实时日志时间:将固定的$dtNow改为每次调用Write-Log时获取当前时间,保证日志时间的准确性;
  3. 目录检查:添加日志目录存在性检查,避免因目录不存在导致日志写入失败;
  4. 错误处理增强:读取文件时添加-ErrorAction Stop确保异常能被捕获,移动文件时添加-Force支持覆盖同名文件(可根据需求调整);
  5. 代码清理:移除冗余的变量赋值和无关断点,让脚本更简洁。

内容的提问来源于stack exchange,提问作者Roy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:03:23