Powershell 2 copy-item which creates a folder if doesn't exist
Asked Answered
I

11

79
$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?

Impiety answered 23/4, 2010 at 0:50 Comment(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?Ln
I
87

Yes, add the -Force parameter.

copy-item $from $to -Recurse -Force
Injunction answered 23/4, 2010 at 7:36 Comment(3)
PowerShell is so easy when one knows it :)Whitson
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, GaryDrillmaster
I ran into this same problem and solved it by calling New-Item first: New-Item -Force $to Copy-Item -Force $from $toCounterpunch
Q
121

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.

Quantic answered 13/1, 2012 at 12:45 Comment(3)
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.Indignation
I also encounter the wrong behavior when the target folder does not existLatialatices
Thanks. This was the answer I was looking for. To bad there not switch to create destination folder.Lolalolande
I
87

Yes, add the -Force parameter.

copy-item $from $to -Recurse -Force
Injunction answered 23/4, 2010 at 7:36 Comment(3)
PowerShell is so easy when one knows it :)Whitson
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, GaryDrillmaster
I ran into this same problem and solved it by calling New-Item first: New-Item -Force $to Copy-Item -Force $from $toCounterpunch
H
20

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.

Hardboiled answered 17/2, 2016 at 16:9 Comment(1)
This works great for local copy thanks! Not when using -ToSession command though :'(Consciencestricken
A
4
$file | Copy-Item -Destination (New-Item -Force -Type Directory -Path $directoryName)
Antidote answered 12/3, 2021 at 0:0 Comment(0)
H
3
  $filelist | % {
    $file = $_
    mkdir -force (Split-Path $dest) | Out-Null
    cp $file $dest
  } 
Herculean answered 1/7, 2016 at 11:48 Comment(0)
G
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
    } 
}
Getraer answered 1/6, 2017 at 18:17 Comment(1)
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.Project
F
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 }
}
Figment answered 9/3, 2019 at 1:40 Comment(0)
D
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
Dark answered 9/9, 2016 at 14:27 Comment(1)
You can use the [System.IO.Directory]::CreateDirectory($targetFolderPath) method, that simply creates the full path (several level of folders) if required, see https://mcmap.net/q/304289/-whats-the-easiest-way-to-ensure-folder-exist-before-i-do-a-file-move-duplicatePipage
E
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.

Exorbitance answered 25/3, 2016 at 22:15 Comment(0)
K
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
    }
Kathyrnkati answered 14/11, 2020 at 22:7 Comment(0)
C
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.

Clarissaclarisse answered 1/2, 2023 at 16:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.