PowerShell 批量文件分发脚本

最近遇到了一个需要批量处理的问题,需要从Excel文件去读取Excel中的计算机名称,手动一个个复制太不方便了;然后根据微软的Powershell命令,进行了学习,然后今天来分析一段实用的 PowerShell 脚本,它能从 Excel 读取计算机名,然后批量向这些计算机复制文件,大大提高工作效率。

加载必要的程序集

Add-Type -AssemblyName Microsoft.Office.Interop.Excel

这行代码的作用是加载 Microsoft.Office.Interop.Excel 程序集。因为后续要对 Excel 文件进行操作,而这个程序集提供了与 Excel 交互的相关功能,有了它,脚本才能正常读取 Excel 中的数据。

定义路径

$sourceFolderPath = "文件路径"$excelFilePath = "C:\path\to\your\excel_file.xlsx"

这里分别定义了源文件夹路径和 Excel 文件路径。源文件夹路径指向了存放要复制文件的位置,Excel 文件路径则指定了包含计算机名列表的 Excel 文件所在之处,为后续的文件读取和复制操作明确了来源。

操作 Excel 应用

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
$workbook = $excel.Workbooks.Open($excelFilePath)
$worksheet = $workbook.Sheets.Item(1)
$usedRange = $worksheet.UsedRange

这部分代码首先创建了一个 Excel 应用实例,将其设置为不可见(Visible = $false)且不显示警告(DisplayAlerts = $false),这样可以在后台安静地进行操作,不干扰用户。然后打开指定的 Excel 工作簿,获取第一个工作表以及工作表的已使用范围,为读取计算机名做好准备。

读取计算机名

$computerNames = @()
$row = 2  # 从第2行开始(A2单元格)
while ($worksheet.Cells.Item($row, 1).Text -ne "") {
    $computerName = $worksheet.Cells.Item($row, 1).Text
    $computerNames += $computerName
    $row++
}

脚本从 Excel 的 A2 单元格开始读取数据,将读取到的计算机名存储到数组$computerNames中,直到遇到空行为止。这样就获取了所有需要进行文件复制操作的目标计算机名称。

清理 Excel 资源

$workbook.Close()
$excel.Quit()
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($worksheet) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($workbook) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

读取完计算机名后,关闭工作簿和 Excel 应用程序,并释放相关的 COM 对象,同时进行垃圾回收。这一系列操作是为了释放系统资源,避免资源占用和内存泄漏。

检查计算机连通性并复制文件

$ping = New-Object System.Net.NetworkInformation.Ping
foreach ($targetComputerName in $computerNames) {
    $targetPath = "\\$targetComputerName\c$"
    try {
        $pingResult = $ping.Send($targetComputerName, 1000)
        if ($pingResult.Status -ne "Success") {
            Write-Output "计算机 $targetComputerName 无法访问,跳过..."
            continue
        }
    } catch {
        Write-Output "检查计算机 $targetComputerName 连通性时出错,跳过: $_"
        continue
    }
    try {
        if (!(Test-Path $targetPath -ErrorAction SilentlyContinue)) {
            Write-Output "无法访问 $targetPath,跳过此计算机..."
            continue
        }
        Copy-Item -Path "$sourceFolderPath\*" -Destination $targetPath -Recurse -Force
        Write-Output "已将所有文件复制到 $targetPath"
    } catch {
        Write-Output "复制文件到 $targetPath 失败:$_"
    }
}

首先创建 ping 对象用于检查计算机连通性。然后遍历所有读取到的计算机名,对每台计算机,先通过 ping 检查其是否可达,如果不可达则跳过。对于可达的计算机,再检查目标共享路径是否可访问,若不可访问也跳过。只有当计算机可达且目标路径可访问时,才将源文件夹中的所有文件复制到目标路径,并输出相应的操作结果。若过程中出现错误,也会及时捕获并输出错误信息。

完整代码

# 加载必要的程序集
Add-Type -AssemblyName Microsoft.Office.Interop.Excel

# 源文件夹路径
$sourceFolderPath = "需要复制的文件夹路径"

# Excel文件路径
$excelFilePath = "Excel文件路径"

# 创建Excel应用实例
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false

# 打开工作簿
$workbook = $excel.Workbooks.Open($excelFilePath)

# 获取第一个工作表
$worksheet = $workbook.Sheets.Item(1)

# 获取已使用范围
$usedRange = $worksheet.UsedRange

# 从A2开始读取计算机名,直到遇到空行为止
$computerNames = @()
$row = 2  # 从第2行开始(A2单元格)

while ($worksheet.Cells.Item($row, 1).Text -ne "") {
    $computerName = $worksheet.Cells.Item($row, 1).Text
    $computerNames += $computerName
    $row++
}

# 关闭工作簿和Excel应用
$workbook.Close()
$excel.Quit()

# 释放COM对象
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($worksheet) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($workbook) | Out-Null
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

# 创建ping对象
$ping = New-Object System.Net.NetworkInformation.Ping

# 遍历计算机名并复制文件
foreach ($targetComputerName in $computerNames) {
    # 目标路径
    $targetPath = "\\$targetComputerName\c$"
    
    # 检查计算机是否可达
    try {
        $pingResult = $ping.Send($targetComputerName, 1000)  # 超时时间1秒
        
        if ($pingResult.Status -ne "Success") {
            Write-Output "计算机 $targetComputerName 无法访问,跳过..."
            continue
        }
    } catch {
        Write-Output "检查计算机 $targetComputerName 连通性时出错,跳过: $_"
        continue
    }
    
    try {
        # 检查目标共享是否可访问
        if (!(Test-Path $targetPath -ErrorAction SilentlyContinue)) {
            Write-Output "无法访问 $targetPath,跳过此计算机..."
            continue
        }
        
        # 复制源文件夹中的所有文件到目标路径
        Copy-Item -Path "$sourceFolderPath\*" -Destination $targetPath -Recurse -Force
        Write-Output "已将所有文件复制到 $targetPath"
    } catch {
        Write-Output "复制文件到 $targetPath 失败:$_"
    }
}