Как я могу скопировать только самый последний файл из каждой папки и подпапки, сохранив при этом структуру каталогов?

Я работаю с каталогом со структурой: файлы года имени учетной записи.

В каждой группе файлов по году и имени обычно есть один или несколько файлов, которые содержат "*Data Rec*" в имени файла. Я хотел бы взять только самый последний файл с таким именем для каждой группы файлов. Прямо сейчас у меня есть приведенный ниже код, который работает, но возвращает только один файл, хотя есть папки для 2012, 2013 и 2014 годов, каждая из которых содержит хотя бы один файл "*Data Rec*". Также он не сохраняет структуру каталогов. Он только бросает файл в место назначения. Файл находится в папке 2014 года, поэтому я хотел бы, чтобы мой пункт назначения содержал папку 2014 года с файлом "*Data Rec*" внутри.

Get-ChildItem -Path $path -Recurse -Filter "*Data Rec*" |
  Sort-Object LastAccessTime -Descending |
  Select-Object -First 1 |
  % { Copy-Item -Path $_.FullName -Destination $destination }

В конце концов я хотел бы также иметь возможность искать самый последний файл "*Analysis*", если выбранная папка не содержит файл "*Data Rec*".


person Cole    schedule 23.02.2016    source источник
comment
Лично я бы использовал PS, чтобы получить желаемый исходный путь и путь назначения, а затем выполнить RoboCopy или что-то еще, чтобы выполнить фактическую копию файла, поскольку он будет перезаписывать файлы только при их обновлении и будет поддерживать структуру каталогов и все такое.   -  person TheMadTechnician    schedule 24.02.2016
comment
@TheMadTechnician Есть ли способ интегрировать параметры Get-ChildItem с robocopy? Я не могу найти способ использовать robocopy для получения только самого последнего файла из каждой папки с этим критерием фильтрации.   -  person Cole    schedule 24.02.2016
comment
Черт, я сказал RoboCopy? Я имею в виду RichCopy. Любимый проект сотрудника Microsoft несколько лет назад, который копирует файлы. Это как RoboCopy, но более круто.   -  person TheMadTechnician    schedule 24.02.2016


Ответы (2)


Используйте два оператора Get-ChildItem, один для перечисления папок, другой для перечисления файлов в каждой папке.

Get-ChildItem -Path $path -Recurse -Directory | ForEach-Object {
  Get-ChildItem -File -Filter "*Data Rec*" |
    Sort-Object LastAccessTime -Descending |
    Select-Object -First 1 |
    Copy-Item -Destination $destination
}

Если вы не используете PowerShell v3 или новее, вам необходимо заменить параметры -Directory и -File фильтрами Where-Object, проверяющими свойство PSIsContainer объектов.

Чтобы сохранить структуру папок, вам нужно удалить $path из полного пути к копируемому файлу и добавить его к пути назначения.

{ Join-Path $destination ($_.FullName.Replace("$path\", '')) }
person Ansgar Wiechers    schedule 24.02.2016
comment
Спасибо за ваш ответ. Я не работаю с PowerShell v3, и у меня возникают проблемы с заменой параметров -Directory и -File. Я понимаю, что для -Directory я хочу заменить его на "Where-Object {$_.PSIsContainer}", но я не уверен, как заменить -File. - person Cole; 26.02.2016
comment
@Cole Просто для ясности: заменив параметры, я не имел в виду буквально заменить их на месте, а удалить их и добавить фильтр Where-Object в конвейер. Что касается сопоставления файлов, то вместо того, чтобы сопоставлять элементы, являющиеся контейнерами (т. е. каталоги), вам просто нужно сопоставлять элементы, которые не являются контейнерами. - person Ansgar Wiechers; 26.02.2016

Я знаю, что это немного некропост, но решение, опубликованное выше Ансгаром, не совсем сработало для меня. Мне пришлось настроить его, чтобы он работал правильно. Не совсем уверен, должен ли я публиковать это как новый ответ или как комментарий к его ответу, поэтому, если я делаю это неправильно, сообщите мне, и как это исправить!

Вот код, который я в итоге использовал. Он берет самый новый файл в каждом подкаталоге и копирует его в место назначения. Он также сначала создает целевой каталог, если он не существует. Я не знаю, как заставить скрипт работать с несколькими строками, так что это будет выглядеть некрасиво — извините! Наконец, в исходном примере человек искал другой термин, чем я (я искал «*bak») — просто замените поисковый запрос тем, что ищется.

Get-ChildItem -Path $Path -Directory | ForEach-Object {Get-ChildItem -Path "$Path\$_" -File -Filter "*bak" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Copy-Item -Force -Destination (New-Item -Force -Type Directory -Path (Join-Path $DestinationPath ($_.FullName.Replace("$Path\", ''))))}

В идеальном мире сценарий не скопировал бы файл, если бы он уже существовал. Это выполнимо, просто удалив -Force, но если я это сделаю, вместо этого он выдаст уродливые сообщения об ошибках. Тем не менее, возможно, это лучше, чем перезапись. Странно, однако, что Copy-Item не позволяет явно не копировать, если имя целевого файла существует.

person Kiran Ramaswamy    schedule 31.08.2018