Как скопировать определенные файлы (без иерархии папок), но не перезаписывать существующие файлы?
Мне нужно скопировать все *.doc
файлы (но не папки, имена которых соответствуют *.doc
) из сетевой папки \\server\source
(включая файлы во всех вложенных папках) в локальную папку C:\destination
без сохранения вложенных папок иерархии (т.е. все файлы должны идти непосредственно в C:\destination
, и никакие вложенные папки не должны создаваться в C:\destination
). Если есть несколько файлов с одинаковыми именами из разных подпапок \\server\source
, только первый должен быть скопирован и никогда не перезаписан - все конфликтующие файлы, найденные позже, должны быть пропущены (может быть много случаев, подобных этому, и пропущенные файлы не должны быть перенесены по сети, иначе это займет слишком много времени). Вот моя попытка реализовать его в PowerShell:
cp \\server\source\* -Recurse -Include *.doc -Container:$false -Destination C:\destination
Есть две проблемы с этой командой:
- Он копирует папки, имена которых также соответствуют
*.doc
.
- В случае конфликтующих имен любой файл, найденный позже, передается по сети и перезаписывает предыдущий файл.
Можете ли вы предложить, как исправить эти проблемы?
Реализации с использованием copy
, xcopy
, robocopy
, cscript
или *.bat
, *.cmd
также приветствуются.
Локальная ОС - это Windows 8, а файловая система - NTFS.
Ответы
Ответ 1
Я сначала создаю список файлов и проверю, как вы проходите через список.
Что-то вроде этого:
$srcdir = "\\server\source\";
$destdir = "C:\destination\";
$files = (Get-ChildItem $SrcDir -recurse -filter *.doc | where-object {-not ($_.PSIsContainer)});
$files|foreach($_){
if (!([system.io.file]::Exists($destdir+$_.name))){
cp $_.Fullname ($destdir+$_.name)
};
}
Итак, используйте Get-ChildItem
для отображения файлов в исходной папке, соответствующих фильтру, через where-object
, чтобы вырезать каталоги.
Затем пройдите через каждый файл в цикле foreach
и проверьте, существует ли имя файла (не полное имя) в целевом объекте с помощью метода Exists
класса system.io.file
.NET.
Если это не так, скопируйте, используя только оригинальное имя файла (отбрасывая исходный путь).
Используйте параметр -whatif
на копии при тестировании, поэтому он отображает только то, что он сделал бы, в случае, если результат не тот, который вы хотели:-)
Ответ 2
Предыдущие ответы кажутся мне слишком сложными, если я что-то не понимаю. Это должно работать:
Get-ChildItem "\\server\source\" *.doc -Recurse | ?{-not ($_.PSIsContainer -or (Test-Path "C:\Destination\$_"))} | Copy-Item -Destination "C:\Destination"
Ни одна из встроенных команд - copy, xcopy или robocopy - будет делать то, что вы хотите самостоятельно, но есть утилита с именем xxcopy, которая будет удобно доступна в http://www.xxcopy.com. Он имеет ряд встроенных опций, специально предназначенных для выравнивания деревьев каталогов в один каталог. Ниже описано, что вы описали:
xxcopy "\\server\source\*.doc" "C:\Destination" /SGFO
Тем не менее, xxcopy имеет различные другие опции для обработки повторяющихся имен файлов, чем просто копирование первой, с которой они столкнулись, например, добавление имени исходного каталога в имя файла или добавление последовательного числа для всех, кроме первого, или всего, кроме самого нового или самый старый. Подробнее см. на этой странице: http://www.xxcopy.com/xxcopy16.htm
Ответ 3
# Get all *.doc files under \\server\source
Get-ChildItem -Path \\server\source *.doc -Recurse |
# Filter out directores
Where-Object { -not $_.PsIsContainer } |
# Add property for destination
Add-Member ScriptProperty -Name Destination -Value { Join-Path 'C:\destination' $this.Name } -PassThru |
# Filter out files that exist on the destination
Where-Object { -not (Test-Path -Path $_.Destination -PathType Leaf } |
# Copy.
Copy-Item
Ответ 4
Зачем использовать foreach, когда у вас уже есть конвейер? Вычисляемые свойства для победы!
Get-ChildItem -Recurse -Path:\\Server\Path -filter:'*.doc' |
Where { -not $_.PSIsContainer } |
Group Name |
Select @{Name='Path'; Expression={$_.Group[0].FullName}},@{Name='Destination'; Expression={'C:\Destination\{0}' -f $_.Name}} |
Copy-Item
Ответ 5
$docFiles = Get-ChildItem -Path "\\server\source" -Recurse | Where-Object {$_.Attributes.ToString() -notlike "*Directory*" -and ($_.Name -like "*.doc" -or $_.Name -like "*.doc?")} | Sort-Object -Unique;
$docFiles | ForEach-Object { Copy-Item -Path $_.fullname -Destination "C:\destination" };
Первая строка считывает каждый файл *.doc и *.doc? (поэтому он также рассматривает формат Office 2010.docx), за исключением Каталогов и дубликатов файлов.
Вторая строка копирует каждый элемент из пункта назначения в источник (папка C:\destination должна уже существовать).
В общем, я предлагаю вам разделить команду на несколько строк, потому что проще создать код (в этом случае первая задача: получить файлы, вторая задача: скопировать файлы).