PowerShell 中使用Copy-Item将一个项目从一个位置复制到另一个位置,可以是复制单个文件、也可以复制文件夹。

复制文件和文件夹

最简单的复制一个文件到另一个文件夹内,如果目标文件夹不存在会自动创建。

Copy-Item "E:\PowerShell\form\1.txt" -Destination "E:\PowerShell\to\"

如果-Destination参数是文件名,就会重命名文件。

Copy-Item "E:\PowerShell\form\1.txt" -Destination "E:\PowerShell\to\2.txt"

使用通配符可以复制文件夹内所有文件到另一个文件内,加上-Recurse就能复制所有的文件和子文件夹的内容。

Copy-Item "E:\PowerShell\form\*" -Destination "E:\PowerShell\to\" -Recurse


注意,在复制文件夹时,当 to 文件夹不存在时,效果和上面一样,但实际功能是复制 from 文件夹并且重命名为 to
当 to 文件夹已存在时,最终的结果目录结构会是 E:\PowerShell\to\from\,也就是把整个 from 文件夹复制到 to 文件夹内。

Copy-Item "E:\PowerShell\form\" -Destination "E:\PowerShell\to\ -Recurse"

排除和包含

使用-Filter参数可以设置复制特定文件名的文件或文件夹,如下例子,如果文件夹名称是 *.txt 也会被复制。
注意,使用此参数时目标文件夹 to 必须是存在的,否则报错。

Copy-Item "E:\PowerShell\from\*" -Destination "E:\PowerShell\to\" -Filter '*.txt'

使用-Exclude-Include参数来排除或包含文件或文件夹,值是单个文件名,或者是包含多个文件名称的数组,同样这里的文件名也包含文件夹名。

Copy-Item "E:\PowerShell\from\*" -Destination "E:\PowerShell\to\" -Exclude '*.pdf'

不复制空文件夹

可以通过判断文件夹内项目数量来确定文件夹是否为空。
首先通过Get-ChildItem $from -Recurse获取文件夹内所有的项目。
然后遍历每个项目,通过$item.PSIsContainer判断是否是文件夹,如果是文件夹且项目个数为 0 则跳过,否则就复制。
由于是按照单个项目来复制,所以需要通过替换原目录为新目录来设置目标路径。

$from = "E:\PowerShell\from\"
$to = "E:\PowerShell\to\"
$items = Get-ChildItem $from -Recurse
ForEach ($item in $items)
{
    if($item.PSIsContainer -and (Get-ChildItem $($item.FullName)).Count -eq 0)
    {
        Continue
    }else{
        $t = $item.Fullname.replace("$from","$to")
        Copy-Item $item.FullName -Destination $t
    }
}

跳过和覆盖

当复制单个文件时默认会直接覆盖同名文件,如果想跳过需要通过Test-Path来判断是否存在同名文件。

if(!(Test-Path "E:\PowerShell\to\1.txt")){
    Copy-Item "E:\PowerShell\from\1.txt" "E:\PowerShell\to\1.txt"
}else{
    Write-Host "包含同名文件"
}

但是当使用通配符复制文件夹内所有内容时,同名的文件夹默认并不会覆盖,而且会报错,这个时候加上-Force参数即可。

Copy-Item "E:\PowerShell\from\*" "E:\PowerShell\to\" -Recurse -Force`

显示复制进度

Copy-Item 并没有参数来设置显示复制进度的功能,要显示进度需要用到Write-Progress
显示复制单个文件的进度

function Copy-File {
    param( [string]$from, [string]$to)
    $ffile = [io.file]::OpenRead($from)
    $tofile = [io.file]::OpenWrite($to)
    try {
        [byte[]]$buff = new-object byte[] 4096
        [long]$total = [int]$count = 0
        do {
            $count = $ffile.Read($buff, 0, $buff.Length)
            $tofile.Write($buff, 0, $count)
            $total += $count
            if ($total % 1mb -eq 0) {
                Write-Progress -Activity "从[$from]复制到[$to]" -status "复制中" `
                   -PercentComplete ([long]($total * 100 / $ffile.Length))
            }
        } while ($count -gt 0)
    }
    finally {
        $ffile.Dispose()
        $tofile.Dispose()
    }
}
$from = "E:\PowerShell\from\1.txt"
$to = "E:\PowerShell\to\1.txt"
Copy-File $from $to

显示复制多个文件的进度

function Copy-File{
    Param([string]$from,[string]$to)
    $Filelist=Get-Childitem "$from" –Recurse
    $Total=$Filelist.count
    $Position=0

    foreach ($File in $Filelist)
    {
        $Filename=$File.Fullname.replace($from,'')
        $DestinationFile=($to+$Filename)
        Write-Progress -Activity "从[$from]复制到[$to]" -Status "正在复制[$Filename]" -PercentComplete (($Position/$total)*100)
        Copy-Item $File.FullName -Destination $DestinationFile
        $Position++
    }

}
$from = "E:\PowerShell\from\"
$to = "E:\PowerShell\to\"
Copy-File $from $to

参考:
https://www.spguides.com/powershell-copy-item/
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/copy-item

标签:Windows, PowerShell