Copy Files Efficiently with PowerShell Copy-Item: A Comprehensive Guide

Published:16 June 2019 - 4 min. read

Today’s sponsor is n8n, the AI-native workflow automation tool built for ITOps and DevSecOps. With 100+ templates to get you started quickly and a powerful visual editor, you can automate complex workflows without giving up control. Check it out here.

 

 

 

 

 

Copying files. It’s not sexy but has to be done. In the GUI, we copy and paste with the clipboard but in PowerShell we have a cmdlet called Copy-Item.

Commands for copying files have been around forever in all shell languages. In PowerShell land, the most popular way to get a copy of a file or folder in your PowerShell script from point A to point B is by using the PowerShell Copy-Item cmdlet. This cmdlet allows us to copy a file and folder while giving us the ability to recurse files in a folder, use wildcards to select the files we need to copy and even use PowerShell Remoting for a file copy!

This cmdlet is a part of the PowerShell provider cmdlets. It’s a generic cmdlet that recognized by its Item noun. Most of these provider cmdlets can be used across different providers but in my nearly 10 years of using PowerShell, I’ve only seen Copy-Item to be used with the file system provider.

By using this cmdlet PowerShell allows a developer to copy files and folders a number of different ways.

Basic Usage

At it’s most basic, the Copy-Item cmdlet copies a single file from point A to point B using the Path parameter as the source file path and the Destination parameter as the destination folder path.

PS> Test-Path -Path C:\PointB\1.txt
False
PS> Copy-Item -Path C:\PointA\1.txt -Destination C:\PointB\
PS> Test-Path -Path C:\PointB\1.txt
True 

This cmdlet can also copy empty folders as well. I’ll list item in the C:\EmptyFolder folder and then copy those out.

PS> Get-ChildItem -Path C:\EmptyFolder\
PS> Test-Path -Path C:\PointB\EmptyFolder -PathType Container
False
PS> Copy-Item -Path C:\EmptyFolder\ -Destination C:\PointB\
PS> Test-Path -Path C:\PointB\EmptyFolder -PathType Container
True 

Perhaps there is a read-only file in the folder. By default, Copy-Item will not overwrite it. To force the override, just add the Force parameter.

Getting Selective with Copy-Item

In addition to copying a single file or folder, we can also copy the entire contents of a folder. The Path parameter of Copy-Item accepts wildcard characters like the asterisk to match one or more characters or the question mark to only match a single character.

PS> @(Get-ChildItem -Path C:\PointB).Count
0
PS> @(Get-ChildItem -Path C:\PointA).Count
10000
PS> @(Get-ChildItem -Path C:\PointB).Count
0
PS> Copy-Item -Path C:\PointA\* -Destination C:\PointB\
PS> @(Get-ChildItem -Path C:\PointB).Count
10000
PS> @(Get-ChildItem -Path C:\PointB).Count
0
PS> Copy-Item -Path 'C:\PointA\26?0.txt' -Destination C:\PointB\
PS> Get-ChildItem -Path C:\PointB\

Directory: C:\PointB
Mode                LastWriteTime         Length Name
-a----        8/11/2017   8:59 AM              5 2600.txt
-a----        8/11/2017   8:59 AM              5 2610.txt
-a----        8/11/2017   8:59 AM              5 2620.txt
-a----        8/11/2017   8:59 AM              5 2630.txt
-a----        8/11/2017   8:59 AM              5 2640.txt
-a----        8/11/2017   8:59 AM              5 2650.txt
-a----        8/11/2017   8:59 AM              5 2660.txt
-a----        8/11/2017   8:59 AM              5 2670.txt
-a----        8/11/2017   8:59 AM              5 2680.txt
-a----        8/11/2017   8:59 AM              5 2690.txt

Merging Multiple Folders Together

Another cool feature of Copy-Item is it’s ability to copy multiple folders together at the same time. By passing multiple paths to the Path parameter, Copy-Item will look at each one, copy either the folder or file(s) depending on the path and “merge” them all into the single destination.

PS> Copy-Item -Path C:\PointB\*,C:\PointC\*,C:\PointD\* -Destination C:\PointE
PS> Get-ChildItem -Path C:\PointE

Directory: C:\PointE
Mode                LastWriteTime         Length Name
-a----       11/11/2017  12:15 PM              2 PointBFile.txt
-a----       11/11/2017  12:15 PM              2 PointCFile.txt
-a----       11/11/2017  12:16 PM              4 PointDFile.txt

Copying Files Recursively

Chances are you’re not going to get lucky and have all files in a single folder with no folders in there as well. We usually run into situations where we’ve got lots of subfolders in the parent folder which files in them too we’d like to copy over. By using the Recurse parameter on Copy-Item, it will gladly look in each subfolder and copy all files and folders in each recursively.

Notice here that I’m piping files and folders from Get-ChildItem directly to Copy-Item. Copy-Item has pipeline support!

PS> (Get-ChildItem -Path C:\PointB\ -Recurse).Count
5
PS> Get-ChildItem -Path C:\PointB\ | Copy-Item -Destination C:\PointC -Recurse
PS> (Get-ChildItem -Path C:\PointC\ -Recurse).Count
5

Advantages of using the PassThru Parameter

Many cmdlets in PowerShell have a PassThru parameter. Cmdlets that typically return nothing can return the objects they are manipulating using the PassThru parameter. This cmdlet is no different. When I first started scripting, I never used this parameter because I didn’t feel I needed to.

For example, if I wanted to copy a file to a remote location and then reference that file later on in my script I’d do something like this:

$remoteFilePath = '\WEBSRV1\c$\File.txt'
Copy-Item -Path C:\File.txt -Destination $remoteFilePath
Write-Host "I've just copied the file to $remoteFilePath"

This method works but it can be better. Instead of defining a variable for the remote path, why not just capture the object that gets returned from Copy-Item cmdlet when using the PassThru parameter instead? The objects returned will always have the destination file path.

$copiedFile = Copy-Item -Path C:\File.txt -Destination '\WEBSRV1\c$'

Copying Files Using a PowerShell Remoting Session

One cool feature that came with PowerShell v5 is this cmdlet’s ability to not use the default SMB protocol for transferring a file but instead use WinRM and a PowerShell remote session. By using the Session parameter, Copy-Item uses an existing PowerShell session and transfers the files that way. This is a great way to get around firewalls and when the session communication is encrypted, an extra layer of security as well.

PS> $session = New-PSSession -ComputerName WEBSRV1
PS> Invoke-Command -Session $session -ScriptBlock { Test-Path -Path C:\File.txt }
False
PS> Copy-Item -Path C:\File.txt -ToSession $session -Destination 'C:\'
PS> Invoke-Command -Session $session -ScriptBlock { Test-Path -Path C:\File.txt }
True

We could have copied the File.txt file over SMB and hoped the C$ admin share was available and used the destination path of \\WEBSRV1\c$. Since we used the ToSession parameter instead, the destination path will always be the path local to the computer the remote session is running under.

Summary

The Copy-Item cmdlet is one of those core PowerShell cmdlets that you’ll use over and over again. In PowerShell, copy files, folders in a number of different ways with it’s simple yet powerful especially with its ability to use wildcards, to merge multiple folders of files together and to use existing PowerShell Remoting sessions!

Further Reading

Copy-item
Copy-item

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!