84
$from = "\\something\1 XLS\2010_04_22\*"
$to =  "c:\out\1 XLS\2010_04_22\"
copy-item $from $to -Recurse 

This works if c:\out\1 XLS\2010_04_22\ does exist . Is it possible with a single command to create c:\out\1 XLS\2010_04_22\ if it doesn't exist?

1
  • Does this need to be using a powershell cmdlet, or are you okay with using xcopy which is available in a powershell prompt, but is not a powershell cmdlet? Commented Jan 4, 2023 at 16:34

11 Answers 11

124

In PowerShell 2.0, it is still not possible to get the Copy-Item cmdlet to create the destination folder, you'll need code like this:

$destinationFolder = "C:\My Stuff\Subdir"

if (!(Test-Path -path $destinationFolder)) {New-Item $destinationFolder -Type Directory}
Copy-Item "\\server1\Upgrade.exe" -Destination $destinationFolder

If you use -Recurse in the Copy-Item it will create all the subfolders of the source structure in the destination but it won't create the actual destination folder, even with -Force.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for this observation. I got some inconsistent copy actions; sometimes it copied a subfolder and sometimes it did not. After making sure the destination folder existed this inconsistency was gone.
I also encounter the wrong behavior when the target folder does not exist
Thanks. This was the answer I was looking for. To bad there not switch to create destination folder.
90

Yes, add the -Force parameter.

copy-item $from $to -Recurse -Force

3 Comments

PowerShell is so easy when one knows it :)
That works for entire directories, but what if I'm just copying individual files (so I can have a status)? How do you copy c:\test1\file.txt to c:\test2\file.txt when c:\test2 does not exist? That's what I really need to know how to do. Thanks, Gary
I ran into this same problem and solved it by calling New-Item first: New-Item -Force $to Copy-Item -Force $from $to
21

In PowerShell 3 and above I use the Copy-Item with New-Item.

copy-item -Path $file -Destination (new-item -type directory -force ("C:\Folder\sub\sub\" + $newSub)) -force -ea 0

I haven't tried it in ver 2.

1 Comment

This works great for local copy thanks! Not when using -ToSession command though :'(
5
$file | Copy-Item -Destination (New-Item -Force -Type Directory -Path $directoryName)

What's really good about this is $file could be a path, an array of file objects, or a pipeline outputting stuff to copy, and the directory will only try to create once and they'll all go in there (same will work for Move-Item too should you need)

Comments

3
  $filelist | % {
    $file = $_
    mkdir -force (Split-Path $dest) | Out-Null
    cp $file $dest
  } 

Comments

3
function Copy-File ([System.String] $sourceFile, [System.String] $destinationFile, [Switch] $overWrite) {

    if ($sourceFile -notlike "filesystem::*") {
        $sourceFile = "filesystem::$sourceFile" 
    }

    if ($destinationFile -notlike "filesystem::*") {
        $destinationFile = "filesystem::$destinationFile" 
    }

    $destinationFolder = $destinationFile.Replace($destinationFile.Split("\")[-1],"")

    if (!(Test-Path -path $destinationFolder)) {
        New-Item $destinationFolder -Type Directory
    }

    try {
        Copy-Item -Path $sourceFile -Destination $destinationFile -Recurse -Force
        Return $true 
    } catch [System.IO.IOException] {
        # If overwrite enabled, then delete the item from the destination, and try again:
        if ($overWrite) {
            try {
                Remove-Item -Path $destinationFile -Recurse -Force        
                Copy-Item -Path $sourceFile -Destination $destinationFile -Recurse -Force 
                Return $true
            } catch {
                Write-Error -Message "[Copy-File] Overwrite error occurred!`n$_" -ErrorAction SilentlyContinue
                #$PSCmdlet.WriteError($Global:Error[0])
                Return $false
            }
        } else {
            Write-Error -Message "[Copy-File] File already exists!" -ErrorAction SilentlyContinue
            #$PSCmdlet.WriteError($Global:Error[0])
            Return $false
        }
    } catch {
        Write-Error -Message "[Copy-File] File move failed!`n$_" -ErrorAction SilentlyContinue
        #$PSCmdlet.WriteError($Global:Error[0]) 
        Return $false
    } 
}

1 Comment

Welcome to Stack Overflow! Thank you for this code snippet, which may provide some immediate help. A proper explanation would greatly improve its educational value by showing why this is a good solution to the problem, and would make it more useful to future readers with similar, but not identical, questions. Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply.
3

Here's an example that worked for me. I had a list of about 500 specific files in a text file, contained in about 100 different folders, that I was supposed to copy over to a backup location in case those files were needed later. The text file contained full path and file name, one per line. In my case, I wanted to strip off the Drive letter and first sub-folder name from each file name. I wanted to copy all these files to a similar folder structure under another root destination folder I specified. I hope other users find this helpful.

# Copy list of files (full path + file name) in a txt file to a new destination, creating folder structure for each file before copy
$rootDestFolder = "F:\DestinationFolderName"
$sourceFiles = Get-Content C:\temp\filelist.txt
foreach($sourceFile in $sourceFiles){
    $filesplit = $sourceFile.split("\")
    $splitcount = $filesplit.count
    # This example strips the drive letter & first folder ( ex: E:\Subfolder\ ) but appends the rest of the path to the rootDestFolder
    $destFile = $rootDestFolder + "\" + $($($sourceFile.split("\")[2..$splitcount]) -join "\")
    # Output List of source and dest 
    Write-Host ""
    Write-Host "===$sourceFile===" -ForegroundColor Green
    Write-Host "+++$destFile+++"
    # Create path and file, if they do not already exist
    $destPath = Split-Path $destFile
    If(!(Test-Path $destPath)) { New-Item $destPath -Type Directory }
    If(!(Test-Path $destFile)) { Copy-Item $sourceFile $destFile }
}

Comments

2

My favorite is to use the .Net [IO.DirectoryInfo] class, which takes care of some of the logic. I actually use this for a lot of similar scripting challenges. It has a .Create() method that creates directories that don't exist, without errors if they do.

Since this is still a two step problem, I use the foreach alias to keep it simple. For single files:

[IO.DirectoryInfo]$to |% {$_.create(); cp $from $_}

As far as your multi file/directory match, I would use RoboCopy over xcopy. Remove the "*" from your from and just use:

RoboCopy.exe $from $to *

You can still add the /r (Recurse), /e (Recurse including Empty), and there are 50 other useful switches.

Edit: Looking back at this it is terse, but not very readable if you are not using the code often. Usually I have it split into two, like so:

([IO.DirectoryInfo]$to).Create()
cp $from $to

Also, DirectoryInfo is the type of the Parent property of FileInfo, so if your $to is a file, you can use them together:

([IO.FileInfo]$to).Parent.Create()
cp $from $to

1 Comment

You can use the [System.IO.Directory]::CreateDirectory($targetFolderPath) method, that simply creates the full path (several level of folders) if required, see stackoverflow.com/a/6925583/704022
1

I have stumbled here twice, and this last time was a unique situation and even though I ditch using copy-item I wanted to post the solution I used.

Had a list of nothing but files with the full path and in majority of the case the files have no extensions. the -Recurse -Force option would not work for me so I ditched copy-item function and fell back to something like below using xcopy as I still wanted to keep it a one liner. Initially I tied with Robocopy but it is apparently looking for a file extension and since many of mine had no extension it considered it a directory.

$filelist = @("C:\Somepath\test\location\here\file","C:\Somepath\test\location\here2\file2")

$filelist | % { echo f | xcopy $_  $($_.Replace("somepath", "somepath_NEW")) }

Hope it helps someone.

Comments

0

I modified @FrinkTheBrave 's answer to also create sub-directories, and resolve relative paths:

$src_root = Resolve-Path("./source/path/")
$target_root = Resolve-Path("./target/path/")
$glob_filter = "*.*"
Get-ChildItem -path $src_root -filter $glob_filter -recurse | 
  ForEach-Object {
        $target_filename=($_.FullName -replace [regex]::escape($src_root),$target_root) 
        $target_path=Split-Path $target_filename
        if (!(Test-Path -Path $target_path)) {
            New-Item $target_path -ItemType Directory
        }
        Copy-Item $_.FullName -destination $target_filename
    }

Comments

0

I have used this before:

new-item -path 'd:\path\to\destination' -name 'folder' && robocopy '.\source\folder\path\or\file' 'd:\path\to\destination\folder' /e

the double ampersand ( && ) works for a command prompt and PowerShell. It allows two separate commands to be run consecutively.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.