Элемент копирования Powershell 2, который создает папку, если она не существует
$from = "\\something\1 XLS\2010_04_22\*"
$to = "c:\out\1 XLS\2010_04_22\"
copy-item $from $to -Recurse
Это работает, если c:\out\1 XLS\2010_04_22\ существует. Можно ли с помощью одной команды создать "c:\out\1 XLS\2010_04_22\", если он не существует.
11 ответов
Да, добавить -Force
параметр.
copy-item $from $to -Recurse -Force
В PowerShell 2.0 по-прежнему невозможно получить командлет Copy-Item для создания папки назначения, вам понадобится такой код:
$destinationFolder = "C:\My Stuff\Subdir"
if (!(Test-Path -path $destinationFolder)) {New-Item $destinationFolder -Type Directory}
Copy-Item "\\server1\Upgrade.exe" -Destination $destinationFolder
Если вы используете -Recurse в Copy-Item, он создаст все подпапки исходной структуры в месте назначения, но не создаст фактическую папку назначения, даже с -Force.
В PowerShell 3 и выше я использую Copy-Item с New-Item.
copy-item -Path $file -Destination (new-item -type directory -force ("C:\Folder\sub\sub\" + $newSub)) -force -ea 0
Я не пробовал это в версии 2.
$file | Copy-Item -Destination (New-Item -Force -Type Directory -Path $directoryName)
Вот пример, который работал для меня. У меня был список из 500 конкретных файлов в текстовом файле, содержащий около 100 различных папок, которые я должен был скопировать в резервную копию на случай, если эти файлы понадобятся позже. Текстовый файл содержал полный путь и имя файла, по одному на строку. В моем случае я хотел убрать букву диска и имя первой подпапки из каждого имени файла. Я хотел скопировать все эти файлы в похожую структуру папок в другой корневой папке назначения, которую я указал. Я надеюсь, что другие пользователи найдут это полезным.
# 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 }
}
$filelist | % {
$file = $_
mkdir -force (Split-Path $dest) | Out-Null
cp $file $dest
}
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
}
}
Мой фаворит - использовать класс.Net [IO.DirectoryInfo], который позаботится о некоторой логике. Я на самом деле использую это для многих подобных задач сценариев. У него есть метод.Create(), который создает несуществующие каталоги, без ошибок, если они есть.
Поскольку это все еще двухэтапная проблема, я использую псевдоним foreach для простоты. Для отдельных файлов:
[IO.DirectoryInfo]$to |% {$_.create(); cp $from $_}
Что касается совпадения нескольких файлов / каталогов, я бы использовал RoboCopy поверх xcopy. Удалите "*" из вашего и просто используйте:
RoboCopy.exe $from $to *
Вы все еще можете добавить /r (Рекурс), /e (Рекурс, включая Пустой), и есть 50 других полезных ключей.
Изменить: Оглядываясь назад, это кратко, но не очень читабельно, если вы не часто используете код. Обычно я делю его на две части, вот так:
([IO.DirectoryInfo]$to).Create()
cp $from $to
Кроме того, DirectoryInfo является типом свойства Parent для FileInfo, поэтому, если ваш $to является файлом, вы можете использовать их вместе:
([IO.FileInfo]$to).Parent.Create()
cp $from $to
Я споткнулся здесь дважды, и этот последний раз был уникальной ситуацией, и хотя я отказался от использования copy-item
Я хотел опубликовать решение, которое я использовал.
В списке не было ничего, кроме файлов с полным путем, и в большинстве случаев файлы не имеют расширений. -Recurse -Force
вариант не сработает у меня так что я угробил copy-item
функционировать и отступил к чему-то подобному ниже, используя xcopy, так как я все еще хотел сохранить его одним вкладышем. Первоначально я связал с Robocopy, но, по-видимому, он ищет расширение файла, и, поскольку многие из них не имеют расширения, он считает его каталогом.
$filelist = @("C:\Somepath\test\location\here\file","C:\Somepath\test\location\here2\file2")
$filelist | % { echo f | xcopy $_ $($_.Replace("somepath", "somepath_NEW")) }
Надеюсь, это поможет кому-то.
Я изменил ответ @FrinkTheBrave, чтобы также создать подкаталоги и разрешить относительные пути:
$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
}
Я использовал это раньше:
новый элемент -path 'd:\path\to\destination' -name 'folder' && robocopy '.\source\folder\path\or\file' 'd:\path\to\destination\folder' /e
двойной амперсанд ( && ) работает для командной строки и PowerShell. Это позволяет последовательно запускать две отдельные команды.