I. 源电脑(数据发送方)配置

在传输之前,必须确保两台电脑可以相互访问

1.1 同一网络和工作组

1.1.1 同一网络

确认“源电脑”和“目标电脑”在“同一网段”,同一“专用网络”或“工作网络”。

1.以管理员身份运行 PowerShell 。

2.查看 IP 地址(确保源&目标电脑在同一网段):

1ipconfig

3.获取网络连接的“接口别名”:

1Get-NetConnectionProfile

命令执行后会返回类似以下的结果:

1Name             : 网络
2InterfaceAlias   : Ethernet0
3InterfaceIndex   : 12
4NetworkCategory : Public
5IPv4Connectivity : Internet
6IPv6Connectivity : NoTraffic

记下你要修改的连接的 InterfaceAlias(例如 Ethernet0)和当前的 NetworkCategory

4.设置网络类型

  • 设置为“专用网络”:
1Set-NetConnectionProfile -InterfaceAlias "Ethernet0" -NetworkCategory Private
  • 设置为“公用网络”:
1Set-NetConnectionProfile -InterfaceAlias "Ethernet0" -NetworkCategory Public
  • 设置为“域网络”:

注意: “域网络”类型通常是系统自动检测并设置的,不建议手动强制设置。但如果确实需要(例如在特定虚拟化环境中),可以使用:

1Set-NetConnectionProfile -InterfaceAlias "Ethernet0" -NetworkCategory DomainAuthenticated

5.验证更改:

再次运行 Get-NetConnectionProfile,检查 NetworkCategory 是否已更新。

6.通过本地安全策略(GUI)

按下【Win + R】打开“运行”对话框,输入:secpol.msc ,进入:网络列表管理器策略 → 网络名称 → [ 选择你当前使用的网络 ] → 属性,在“网络位置”选项卡中选择:Private(专用)或 Public(公用)。

1.1.2 启用网络发现和共享

打开“高级共享设置”找到“专用网络”或“工作网络”:

  • ✅启用网络发现(自动设置网络连接的设备)
  • ✅文件和打印机共享

1.1.3 同一工作组

确认“源电脑”和“目标电脑”在同一工作组,一般默认 WORKGROUP 工作组。

打开“运行”,输入:sysdm.cpl ,打开“系统属性”查看‘计算机全名’和‘工作组’。

1.2 创建专用账户

1.按下【Win + R】打开“运行”对话框,输入 lusrmgr.msc 打开 “本地用户和组”控制台,创建数据迁移专用账户,配置参考如下图👇

PIC_20251024_113231.png

2.确认上面创建的专用账户它只属于 Users 组(不要有 Administrators 组权限)

PIC_20251024_113816.png

3.在命令行创建用户。在命令提示符(管理员)或 PowerShell(管理员)中执行

1net user 用户名 密码 /add /passwordchg:no /expires:never

💡默认情况下,新用户会自动加入 Users 组。查看该用户信息

1net user 用户名

确保该用户仅属于 Users 组(如果不在,可手动加入)

1net localgroup Users 用户名 /add

如果该用户还属于 Administrators 或其它组,可通过以下命令删除

1net localgroup Administrators 用户名 /delete

设置用户密码永不过期(可选项)

1wmic useraccount where name='用户名' set PasswordExpires=FALSE

或在 PowerShell 中

1Set-LocalUser -Name "用户名" -PasswordNeverExpires $true

删除用户

1net user 用户名 /delete

1.3 拒绝本地登录

在“运行”对话框中输入 <font style="color:rgb(89, 97, 114);background-color:rgb(241, 243, 244);">secpol.msc</font> 打开“本地安全策略”,配置参考如下图👇

PIC_20251024_114502.png

强制立即更新组策略设置,无需等待自动刷新周期

1gpupdate /force

1.4 不显示在登录界面

以管理员身份打开命令提示符,输入以下命令:

1reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" /v transfer /t REG_DWORD /d 0 /f

若要恢复显示,值设为 1 或删除隐藏项:

1reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" /v transfer /f

1.5 共享待发送目录

1.5.1 设置共享权限

右键打开目标文件夹属性 -> 选择“共享”选项卡:

PIC_20251024_220748.png

1.5.2 设置文件系统权限

打开“安全”选项卡,配置参考如下图👇

PIC_20251024_221003.png

II. 目标电脑(数据接收方)配置

我们需要在这里创建定时任务,因为它将是主动拉取数据的一方。

2.1 同一网络和工作组

1.同一网段:确认“目标电脑”和“源电脑”在"同一网段"。

2.同一网络:确认“目标电脑”和“源电脑”在同一“专用网络”或“工作网络”。

3.同一工作组:确认“目标电脑”和“源电脑”在同一工作组,一般默认为 WORKGROUP 工作组。

2.2 添加 Windows 凭据

在“运行”对话框中输入 <font style="color:rgb(89, 97, 114);background-color:rgb(241, 243, 244);">control.exe keymgr.dll</font> 打开“凭据管理器”,添加在源电脑(数据发送方)上创建的专用账户(服务端名称前不需要添加 \\),配置参考如下图👇

PIC_20251024_230130.png

测试连接,在“运行”对话框中输入:\\源电脑的计算机名或 IP 地址(例如 \\DESKTOP-ABC1234\\192.168.1.100)。若能看到一个文件夹,并且能打开,说明共享和网络连接是正常的;若不能,请参考后续章节排查问题。

2.3 编写 Robocopy 脚本

由于 Robocopy 本身只能按“天”来筛选修改时间(通过 /MINAGE 和 /MAXAGE 参数),不能精确到小时/分钟/秒,实际测试如仅删除昨天(00:00:00 - 23:59:59)的文件,在 Windows 批处理脚本(.bat)中无法实现,建议使用 PowerShell 脚本实现。PowerShell 脚本编码格式建议为 UTF-8 with BOM ,否则控制台和产生的日志会有乱码。

  1<#
  2    PowerShell 自动备份与清理脚本(最终一体版)
  3    功能:
  4      1. 删除目标目录及日志目录中 14 天前文件;
  5      2. 使用 robocopy 按日期区间复制文件;
  6      3. 每个目标目录日志记录删除和复制操作;
  7      4. 支持每日主日志汇总统计(删除文件数/复制文件数);
  8      5. 多线程 robocopy;
  9      6. 测试模式。
 10#>
 11
 12# ======================== 配置区域 ========================
 13$SourceDir1 = "\\SharePC1\Source1"
 14$SourceDir2 = "\\SharePC2\Source2"
 15$SourceDir3 = "\\SharePC3\Source3"
 16
 17$TargetDir1 = "E:\Backup1"
 18$TargetDir2 = "E:\Backup2"
 19$TargetDir3 = "E:\Backup3"
 20
 21$LogDir = "E:\Logs"
 22
 23$DeleteDays = 14
 24$Threads = 16
 25$TestMode = $false  # 设置为 $true 仅打印操作,不实际执行
 26
 27# ======================== 函数定义 ========================
 28
 29function Invoke-ForfilesDelete {
 30    param(
 31        [string]$Path,
 32        [int]$Days,
 33        [string]$OutputLogPath = $null,
 34        [switch]$Quiet
 35    )
 36
 37    if (-not (Test-Path $Path)) { Write-Host "路径不存在,跳过删除: $Path"; return }
 38
 39    $escapedPath = '"' + $Path + '"'
 40    $cmd = "forfiles /P $escapedPath /S /D -$Days /C `"cmd /c del /q @path && echo 已删除: @path`""
 41
 42    if ($TestMode) { Write-Host "[TEST MODE] Would run: $cmd"; return }
 43
 44    $output = cmd.exe /c "$cmd" 2>&1 | Where-Object { ($_ -ne "") -and ($_ -notmatch "用指定的搜索标准没有找到文件") }
 45
 46    $timeHeader = "==== 删除执行时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ===="
 47
 48    if ($Quiet) { return }
 49    elseif ($OutputLogPath) {
 50        $isNewFile = -not (Test-Path $OutputLogPath) -or ((Get-Item $OutputLogPath).Length -eq 0)
 51        if ($isNewFile) {
 52            $logName = Split-Path $OutputLogPath -Leaf
 53            $title = "########## $logName 删除与复制记录 ##########"
 54            $title | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append
 55            "" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append
 56        }
 57
 58        $timeHeader | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append
 59        if ($output) { $output | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
 60        else { "没有找到可删除的文件。" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
 61        "" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append
 62    }
 63    else {
 64        Write-Host $timeHeader
 65        if ($output) { $output | Write-Host }
 66        else { Write-Host "没有找到可删除的文件。" }
 67        Write-Host ""
 68    }
 69}
 70
 71function Copy-FilesByDate {
 72    param(
 73        [string]$SourceDir,
 74        [string]$TargetDir,
 75        [datetime]$StartTime,
 76        [datetime]$EndTime,
 77        [string]$Description,
 78        [string]$OutputLogPath
 79    )
 80
 81    if (-not (Test-Path $SourceDir)) { Write-Host "源目录不存在: $SourceDir"; return }
 82    if (-not (Test-Path $TargetDir)) { New-Item -ItemType Directory -Force -Path $TargetDir | Out-Null }
 83
 84    $timeHeader = "==== 复制执行时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ===="
 85    $files = Get-ChildItem -Path $SourceDir -Recurse -File |
 86        Where-Object { $_.LastWriteTime -ge $StartTime -and $_.LastWriteTime -le $EndTime }
 87
 88    if ($TestMode) {
 89        Write-Host "[TEST MODE] Would copy $($files.Count) files from $SourceDir to $TargetDir"
 90        foreach ($f in $files) { Write-Host "  $($f.FullName)" }
 91        return
 92    }
 93
 94    if ($OutputLogPath) { $timeHeader | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
 95
 96    if ($files.Count -eq 0) {
 97        if ($OutputLogPath) { "没有找到可复制的文件。" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
 98        else { Write-Host "没有找到可复制的文件。" }
 99        return
100    }
101
102    foreach ($f in $files) {
103        $relativePath = $f.FullName.Substring($SourceDir.Length)
104        $destPath = Join-Path $TargetDir $relativePath
105        $destDir = Split-Path $destPath -Parent
106        if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force | Out-Null }
107
108        robocopy (Split-Path $f.FullName -Parent) $destDir $f.Name /R:1 /W:1 /MT:$Threads /NFL /NDL /NJH /NJS /NP | Out-Null
109
110        if ($OutputLogPath) { "已复制: $destPath" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
111    }
112
113    if ($OutputLogPath) { "" | Out-File -FilePath $OutputLogPath -Encoding UTF8 -Append }
114}
115
116# 统计日志文件中的删除数量
117function Get-DeletedStats {
118    param([string]$LogPath)
119    if (-not (Test-Path $LogPath)) { return @{Count=0; SizeMB=0} }
120    $deletedLines = Select-String "已删除:" $LogPath | ForEach-Object { $_.Line }
121    $count = $deletedLines.Count
122    return @{Count=$count}
123}
124
125# 统计日志文件中的复制数量和大小
126function Get-CopiedStats {
127    param([string]$LogPath)
128    if (-not (Test-Path $LogPath)) { return @{Count=0; SizeMB=0} }
129    $copiedLines = Select-String "已复制:" $LogPath | ForEach-Object { $_.Line }
130    $count = $copiedLines.Count
131    $size = 0
132    foreach ($line in $copiedLines) {
133        $filePath = $line -replace "已复制: ",""
134        if (Test-Path $filePath) { $size += (Get-Item $filePath).Length }
135    }
136    return @{Count=$count; SizeMB=[math]::Round($size/1MB,2)}
137}
138
139# ======================== 主程序 ========================
140
141$Today = Get-Date
142$Yesterday = $Today.AddDays(-1)
143$TwoDaysAgo = $Today.AddDays(-2)
144
145if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Force -Path $LogDir | Out-Null }
146
147$LogFile1 = Join-Path $LogDir "Log1_$($Today.ToString('yyyyMMdd')).log"
148$LogFile2 = Join-Path $LogDir "Log2_$($Today.ToString('yyyyMMdd')).log"
149$LogFile3 = Join-Path $LogDir "Log3_$($Today.ToString('yyyyMMdd')).log"
150$MainLog  = Join-Path $LogDir "Main_$($Today.ToString('yyyyMMdd')).log"
151
152Write-Host "========== 开始执行备份与清理 =========="
153
154# 删除旧文件
155Invoke-ForfilesDelete -Path $TargetDir1 -Days $DeleteDays -OutputLogPath $LogFile1
156Invoke-ForfilesDelete -Path $TargetDir2 -Days $DeleteDays -OutputLogPath $LogFile2
157Invoke-ForfilesDelete -Path $TargetDir3 -Days $DeleteDays -OutputLogPath $LogFile3
158
159# 删除旧日志(静默)
160Invoke-ForfilesDelete -Path $LogDir -Days $DeleteDays -Quiet
161
162# 复制文件并写入日志
163Copy-FilesByDate -SourceDir $SourceDir1 -TargetDir $TargetDir1 `
164    -StartTime ($Today.Date) -EndTime $Today -Description "源目录1" -OutputLogPath $LogFile1
165
166Copy-FilesByDate -SourceDir $SourceDir2 -TargetDir $TargetDir2 `
167    -StartTime ($Yesterday.Date) -EndTime ($Today.Date.AddSeconds(-1)) -Description "源目录2" -OutputLogPath $LogFile2
168
169Copy-FilesByDate -SourceDir $SourceDir3 -TargetDir $TargetDir3 `
170    -StartTime ($TwoDaysAgo.Date) -EndTime ($Yesterday.Date.AddSeconds(-1)) -Description "源目录3" -OutputLogPath $LogFile3
171
172# ======================== 汇总主日志 ========================
173
174$stats1Del = Get-DeletedStats $LogFile1
175$stats2Del = Get-DeletedStats $LogFile2
176$stats3Del = Get-DeletedStats $LogFile3
177
178$stats1Copy = Get-CopiedStats $LogFile1
179$stats2Copy = Get-CopiedStats $LogFile2
180$stats3Copy = Get-CopiedStats $LogFile3
181
182$summary = @"
183========== $($Today.ToString('yyyy-MM-dd')) 主日志汇总 ==========
184目标目录1: 删除 $($stats1Del.Count) 个文件, 复制 $($stats1Copy.Count) 个文件, 总大小 $($stats1Copy.SizeMB) MB
185目标目录2: 删除 $($stats2Del.Count) 个文件, 复制 $($stats2Copy.Count) 个文件, 总大小 $($stats2Copy.SizeMB) MB
186目标目录3: 删除 $($stats3Del.Count) 个文件, 复制 $($stats3Copy.Count) 个文件, 总大小 $($stats3Copy.SizeMB) MB
187============================================================
188"@
189
190$summary | Out-File -FilePath $MainLog -Encoding UTF8 -Append
191
192Write-Host "========== 脚本执行完成 =========="

2.4 测试脚本

测试脚本以验证脚本准确性,PowerShell 控制台测试步骤:

1.以管理员身份打开 PowerShell ;

2.首次运行保留 $TestMode = $true ;

3.在 PowerShell 控制台执行脚本(注意替换为你的脚本路径):

1powershell -ExecutionPolicy Bypass -File "E:\Script\backup_task.ps1"

4.查看控制台和日志文件是否符合预期(路径、时间、文件名等);

5.验证无误后改为 $false 再正式执行。

III. 任务计划配置

在“运行”对话框中输入 <font style="color:rgb(89, 97, 114);background-color:rgb(241, 243, 244);">taskschd.msc</font> 打开“任务计划程序”。

1.创建基本任务,输入任务“名称”,描述可选;

2.任务触发器,选择任务执行周期;

3.设置任务开始时间;

4.选择要执行的操作:

  • ✅启动程序;

5.启动程序:

  • 程序或脚本
1C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
  • 添加参数
1-ExecutionPolicy Bypass -File "D:\Scripts\backup_and_cleanup.ps1"

6.点击确定完成后,在任务属性【常规】选项卡中:

  • 运行任务时用户,选择管理员用户,如 Administrator
  • ✅ 不管用户是否登录都要运行;
  • ✅ 使用最高权限运行。

7.在【条件】选项卡中:

  • ❎(取消勾选)“只有在计算机使用交流电源时才启动此任务”