How to copy files from source to destination in PowerShell
Take a closer look at Copy-Item cmdlet coding examples to build advanced PowerShell scripts that copy files with safety measures to ensure the duplicates reach their destinations.
Copying files between folders, drives and machines can be a waste of your time if you do it manually on a regular basis. A bit of PowerShell knowhow automates this tedious process and even handles the most complex situations.
Once you understand the parameters associated with the Copy-Item command and how they work together, you can produce comprehensive scripts with more advanced PowerShell commands to copy files and directories.
All these examples work on both Windows PowerShell and PowerShell 7.
PowerShell has providers -- .NET programs that expose the data in a data store for viewing and manipulation -- and a set of generic cmdlets that work across providers.
These include the following cmdlets:
- *-Item
- *-ItemProperty
- *-Content
- *-Path
- *-Location
Therefore, with a default installation of PowerShell or Windows PowerShell, you can use the Copy-Item cmdlet to copy files, Registry keys and variables. Some modules do include custom providers, such as the Active Directory module, which lets you use those generic cmdlets in the data they expose. If you want to view the PowerShell providers in your PowerShell session, use the following.
Get-PsProvider
For example, in my PowerShell 7 session on Windows 10, I have the following providers.
How do you use the Copy-Item command?
The simplest form of Copy-Item involves a source path and a destination path. If we want to use the FileSystem provider -- which all the following examples will assume -- we specify our paths by starting with a drive letter and a colon.
Using the .\ or ./ to represent the current directory infers the current path based on the current working directory, which doesn't have to be a FileSystem provider path. You can check your current working directory with Get-Location.
The example in the following command copies a single file located at the Path parameter to the path specified in the Destination parameter.
Copy-Item -Path C:\source\path\file.txt -Destination D:\dest\path\text.txt
If you prefer to use a shorter command, PowerShell has several aliases for its major cmdlets. The following command shows the three aliases, copy, cp and cpi, for the Copy-Item cmdlet.
In PowerShell on Linux, the cp alias doesn't exist because there is an existing Linux command named cp.
How can you use PowerShell commands to copy files?
To show how the various Copy-Item parameters work, create a test file with the following command.
Get-Process | Out-File -FilePath c:\test\p1.txt
Use this command to copy a file with the Destination parameter. We aren't specifying a file name in the destination parameter. In this case, it will use the original file name of "p1.txt."
Copy-Item -Path C:\test\p1.txt -Destination C:\test2\
A confusing part of using Copy-Item is that there is no output if the command succeeds.
If you want to take advantage of both the command's alias and position parameters, you can specify the source and destination in order.
Copy C:\test\p1.txt C:\test2\
However, alias use in saved scripts should be avoided for the most part as it isn't considered best practice.
To get feedback from Copy-Item, use the PassThru parameter. This feature returns objects for each of the items that was copied. It's a helpful tool to confirm the command performed properly.
Copy-Item -Path C:\test\p1.txt -Destination C:\test2\ -PassThru
You can also use the Verbose parameter.
The Verbose parameter gives you information as the command executes, whereas PassThru shows the result.
By default PowerShell overwrites the file if a file with the same name exists in the target folder.
If the file in the target directory is set to read-only, you'll get an error.
Copy-Item -Path C:\test\p1.txt -Destination C:\test2\
You need to be a PowerShell Jedi to overcome this. Use the Force parameter.
Copy-Item -Path C:\test\p1.txt -Destination C:\test2\ -Force
PowerShell can rename files as part of the copy process. For example, this code creates nine copies of the p1.txt file named "p2.txt" through "p10.txt."
2..10 | Foreach-Object {
$newname = "p$_.txt"
Copy-Item -Path C:\test\p1.txt -Destination C:\test2\$newname -Verbose
}
In this case, we're using the ".." operator to create an array of integers from two to 10. Then for each of those integers, we're creating a new file with the names that you can see in the verbose output.
How can you use PowerShell commands to copy multiple files or folders?
There are a few techniques to copy multiple files or folders when using PowerShell.
Copy-Item -Path C:\test\*.txt -Destination C:\test2\
Copy-Item -Path C:\test\* -Filter *.txt -Destination C:\test2\
Copy-Item -Path C:\test\* -Include *.txt -Destination C:\test2\
These commands copy all the .txt files from the test folder to the test2 folder, but the Include parameter lets PowerShell be more selective. For example, this command only copies files with "6" in the filename.
Copy-Item -Path C:\test\* -Include *6*.txt -Destination C:\test2\ -PassThru
Copy-Item has an Exclude parameter to reject certain files from the copy operation. This PowerShell command only copies text files that start with the letter P unless there is a "7" in the name.
Copy-Item -Path C:\test\* -Filter p*.txt -Exclude *7*.txt -Destination C:\test2\ -PassThru
Example: Learn to use the Path, Filter, Include or Exclude parameters
You can combine the Path, Filter, Include or Exclude parameters to refine the copy process even further. However if you use Include and Exclude in the same call, PowerShell ignores Exclude. You can also supply an array of filenames. The path is simplified if your working folder is the source folder for the copy.
Copy-Item -Path p1.txt,p3.txt,x5.txt -Destination C:\test2\
The Path parameter also accepts pipeline input.
Get-ChildItem -Path C:\test\p*.txt |
Where-Object {(($_.BaseName).Substring(1,1) % 2 ) -eq 0} |
Copy-Item -Destination C:\test2\
PowerShell checks the p*.txt files in the c:\test folder to see if the second character is divisible by two. If so, PowerShell copies the file to the C:\test2 folder.
If you end up with a folder or file name that contains wildcard characters (*, [, ], ?), use the LiteralPath parameter instead of the Path parameter. LiteralPath treats all the characters as literals and ignores any wildcards.
Example: Recursive copy
To copy a folder and its entire contents, use the Recurse parameter.
Copy-Item -Path c:\test\ -Destination c:\test2\ -Recurse
The recursive copy will work its way through all the subfolders below the c:\test folder. PowerShell will then create a folder named "test" in the destination folder and copy the contents of c:\test into it.
When copying between machines, you can use universal naming convention paths to bypass the local machine.
Copy-Item -Path \\server1\fs1\test\p1.txt -Destination \\server2\arc\test\
Another option is to use PowerShell commands to copy files over a remoting session.
$cred = Get-Credential -Credential W16ND01\Administrator
$s = New-PSSession -VMName W16ND01 -Credential $cred
In this case, we use PowerShell Direct to connect to the remote machine. You'll need the Hyper-V module loaded to create the remoting session over the VMBus. Next, use PowerShell commands to copy files to the remote machine.
Copy-Item -Path c:\test\ -Destination c:\ -Recurse -ToSession $s
You can also copy from the remote machine.
Copy-Item -Path c:\test\p*.txt -Destination c:\test3\ -FromSession $s
The ToSession and FromSession parameters control the direction of the copy and whether the source and destination are on the local machine or a remote one. You can't use ToSession and FromSession in the same command, and you also can't use relative paths.
Example: Advanced techniques to check for errors and resume a copy
The Copy-Item cmdlet lacks error checking or restart capabilities. For those features, you'll need to write the code. Here is a starting point.
function Copy-FileSafer {
[CmdletBinding()]
param (
[string]$path,
[string]$destinationfolder
)
if (-not (Test-Path -Path $path)) {
throw "File not found: $path"
}
$sourcefile = Split-Path -Path $path -Leaf
$destinationfile = Join-Path -Path $destinationfolder -ChildPath $sourcefile
$b4hash = Get-FileHash -Path $path
try {
Copy-Item -Path $path -Destination $destinationfolder -ErrorAction Stop
}
catch {
throw "File copy failed"
}
finally {
$afhash = Get-FileHash -Path $destinationfile
if ($afhash.Hash -ne $b4hash.Hash) {
throw "File corrupted during copy"
}
else {
Write-Information -MessageData "File copied successfully" -InformationAction Continue
}
}
}
This script tests the source file path and calculates the file hash. The file copy process occurs within a try/catch block used for exception handling. So if an error is detected, it will display "File copy failed."
With additional coding, the script can recursively retry several times. After each copy attempt the script can calculate the hash of the file and compare it to the original. If they match, all is well. If not, an error is reported.
Editor's note: Richard Siddaway originally wrote this article, and Anthony Howell has expanded it.