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!