远程非域PC共享文件夹CSV文件监控拷贝PowerShell脚本问题
解决思路:先建立带凭据的网络映射,再使用FileSystemWatcher
FileSystemWatcher本身并不支持直接传递凭据访问非域共享,因为它依赖系统已建立的网络连接上下文。所以核心解决方案是先通过凭据建立到目标共享的网络连接,之后FileSystemWatcher就能正常访问该共享了。
下面是修改后的完整脚本,包含凭据处理、网络映射建立、监控逻辑和连接清理:
# 1. 定义目标共享和相关参数 $remoteShareRoot = '\\remoteip\folder' # 共享根目录,用于建立映射 $watchFolder = "$remoteShareRoot\subfolder" $filter = '*.csv' $destination = '\\mynetworkstorage\folder\' # 2. 创建凭据对象(替换为你的实际账号信息) $remotePCName = "remoteip" # 目标PC的IP或计算机名 $username = "$remotePCName\你的共享账号" $password = Read-Host "请输入共享账号的密码" -AsSecureString $credential = New-Object System.Management.Automation.PSCredential ($username, $password) try { # 3. 建立到目标共享的SMB映射(无需挂载盘符,仅建立连接上下文) Write-Host "正在建立到共享 $remoteShareRoot 的连接..." New-SmbMapping -RemotePath $remoteShareRoot -Credential $credential -Persistent $false | Out-Null # 4. 初始化FileSystemWatcher $fsw = New-Object IO.FileSystemWatcher $watchFolder, $filter -Property @{ IncludeSubdirectories = $true NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite' } # 5. 注册文件创建事件(注意使用$using:引用外部变量) $onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action { $path = $Event.SourceEventArgs.FullPath $name = $Event.SourceEventArgs.Name $changeType = $Event.SourceEventArgs.ChangeType $timeStamp = $Event.TimeGenerated Write-Host "检测到文件 '$name' 已$changeType,时间:$timeStamp" try { Copy-Item $path -Destination $using:destination -Force -Verbose Write-Host "文件 '$name' 已成功拷贝到目标路径" } catch { Write-Error "拷贝文件 '$name' 失败:$_" } } Write-Host "监控已启动,正在等待新增CSV文件...按Ctrl+C停止" # 保持脚本运行,直到用户中断 do { Start-Sleep -Seconds 1 } while ($true) } catch { Write-Error "初始化过程出错:$_" } finally { # 6. 清理资源:停止监控并断开网络连接 Write-Host "正在停止监控并清理连接..." Unregister-Event -SourceIdentifier FileCreated -ErrorAction SilentlyContinue $fsw.Dispose() -ErrorAction SilentlyContinue Remove-SmbMapping -RemotePath $remoteShareRoot -Force -ErrorAction SilentlyContinue Write-Host "资源已清理完成" }
关键细节说明:
- 网络映射的作用:
New-SmbMapping会在系统中建立一个带凭据的临时连接,让后续的FileSystemWatcher和Copy-Item操作能自动使用这个凭据访问共享,无需再次传递。 - $using:变量引用:在
Register-ObjectEvent的Action块中,无法直接访问外部脚本定义的变量(比如$destination),必须用$using:前缀来跨运行空间引用。 - 资源清理:使用
try/finally块确保即使脚本出错或被中断,也会停止监控并断开网络连接,避免残留无效的SMB映射。 - 凭据安全:用
Read-Host -AsSecureString输入密码,避免明文显示;如果需要自动化运行,可以将密码加密存储(比如用ConvertTo-SecureString和Export-Clixml),避免手动输入。
替代方案:使用传统的net use命令
如果你更习惯用net use,可以替换第3步的New-SmbMapping为:
# 转换SecureString为明文(仅用于net use,注意安全风险) $plainPassword = [System.Net.NetworkCredential]::new("", $password).Password net use $remoteShareRoot /user:$username $plainPassword /persistent:no
不过这种方式会将密码转为明文,安全性稍差,优先推荐New-SmbMapping。
内容的提问来源于stack exchange,提问作者AdzzzUK




