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

PowershellCopyPowershell 2.0

Powershell Problem Overview


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

Powershell Solutions


Solution 1 - Powershell

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.

Solution 2 - Powershell

Yes, add the -Force parameter.

copy-item $from $to -Recurse -Force

Solution 3 - Powershell

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.

Solution 4 - Powershell

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

Solution 5 - Powershell

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
    } 
}

Solution 6 - Powershell

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 }
}

Solution 7 - Powershell

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

Solution 8 - Powershell

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.

Solution 9 - Powershell

$file | Copy-Item -Destination (New-Item -Force -Type Directory -Path $directoryName)

Solution 10 - Powershell

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
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMicMitView Question on Stackoverflow
Solution 1 - PowershellFrinkTheBraveView Answer on Stackoverflow
Solution 2 - PowershellShay LevyView Answer on Stackoverflow
Solution 3 - PowershellmjuddView Answer on Stackoverflow
Solution 4 - PowershellmajkinetorView Answer on Stackoverflow
Solution 5 - PowershellBartView Answer on Stackoverflow
Solution 6 - PowershellToby BierlyView Answer on Stackoverflow
Solution 7 - PowershellMichael EricksonView Answer on Stackoverflow
Solution 8 - PowershellATekView Answer on Stackoverflow
Solution 9 - PowershellHashbrownView Answer on Stackoverflow
Solution 10 - PowershellProphet LambView Answer on Stackoverflow