PowerShell offers a whole collection of file management cmdlets. To copy and move files, you can use Copy-Item and Move-Item. Even though those commands offer many options, sometimes manual work is needed for some basic features.

Robocopy is often the best choice ^

In principle, you can use common Windows tools such as xopy or robocopy in PowerShell. However, their integration with the object-oriented PowerShell is only superficial. You can pass only single objects to them through the pipe and, after copying the files, you can only use the return code to determine the success of the task.

When you don’t have to pass information to PowerShell functions, you can work with the more powerful robocopy. Even compared to xcopy, Copy-Item is a relatively primitive tool.

The cmdlets Copy-Item (alias copy, cp) and Move-Item (alias move, mv) behave in a similar way and accept the same parameters with only a few exceptions. Thus, the following examples are about Copy-Item; only if differences to Move-Item exist will I mention them.

Determining source and destination ^

The simplest application is when you just specify the source files and the destination folder. You can omit the corresponding parameters -Path and -Destination. If you don’t specify the destination, Copy-Item will use the current folder.

If the destination is supposed to be a folder that doesn’t exist yet, Copy-Item will create a file with the corresponding name and that will receive the contents of all source files. Because you will rarely need such a result, you always have to ensure that the destination folder exists before you execute the command.

Filtering source files ^

The parameters -Filter, -Include, and -Exclude allow you to narrow down the list of source files. For instance, the combination of -Filter *.j* and -Exclude *.jpg would select all files that begin with the extension “j” but exclude all JPG files.

If you execute the following command to copy all *.ini files to the subfolder config_bak, two things are different from conventional Windows copy tools: The cmdlet doesn’t return any information about the success of the copy process and it won’t ask for confirmation to overwrite existing files with the same name:

Copy-Item *.ini ./config_bak

Displaying information about the copy process

Breaking the silence of Copy-Item is fairly easy by using the -Passthru parameter, which prompts Copy-Item to write information about the file objects to the pipeline. This enables you to retrieve any thinkable details:

Copy *.ini ./config_bak -passthru | foreach{$_.FullName; $len += ($_.length/1KB);}; Write-Host $len "Bytes copied"; $len=0;

This example uses a loop to display all copied file names with their path. The command also adds the size of the files in KB, stores it in the variable $len, and shows the result at the end of the file list. To prevent wrong results in subsequent executions, the command resets $len to 0.

Preventing files from being overwritten ^

Preventing Copy-Item from overwriting existing files is a bit more difficult. The parameter -Confirm is of no help here because Copy-Item will then just prompt you to confirm any copy operation whether a file with the same name exists at the destination or not. Even if a file with the same name exists, you won’t notice it this way.

In this case, Move-Item behaves differently than Copy-Item and refuses to overwrite files and folders.

The Test-Path cmdlet, which determines whether all elements of a file system path exist, allows you to remedy Copy-Item’s shortcoming. However, the command proves to be complicated considering the relatively simple task:

gci *.ini | %{if(-not(Test-Path (".\config_bak\" + $_.name))){copy $_ .\config_bak -passthru|select name}}

In the example, Get-ChildItem (alias gci) first passes all file names to a loop where Test-Path determines, for each file, whether the file exists or not. If the file doesn’t already exist, it will be copied, file by file.

If you have to use Copy-Item regularly and want to avoid accidentally overwriting existing files, typing such a command sequence is too time consuming. A solution could be to write your own function that is based on the previous example.

Copying and moving recursively ^

A common requirement for a copy command is the ability to read the files from an entire directory tree and duplicate them to the destination. For this purpose, Copy-Item offers the parameter -Recursive, which you can shorten to -r

copy -r scripts\* $env:temp

This command copies the contents of the folder scripts with all its subfolders to the folder to which the environment variable %temp% points. If you want scripts to be created below the destination folder, you have to change the command in the following way:

Subscribe to 4sysops newsletter!

copy -r scripts $env:temp

With Move-Item, you don’t need the parameter -Recursive; this cmdlet always moves the entire directory tree below the source folder.


Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2021


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account