PowerShell создает дубликат папки с файлами нулевого размера

Я хочу создать зеркальное изображение набора папок с размером файла 0, но, хотя robocopy действительно хорош, он не сохраняет всю информацию, которую я хотел бы:

robocopy D:\documents E:\backups\documents_$(Get-Date -format "yyyyMMdd_HHmm")\ /mir /create

Переключатель /create заставляет каждый файл в папке-дубликате иметь нулевой размер, и это хорошо, но я бы хотел, чтобы к каждому файлу в папке-дубликате в конец имени добавлялся [size] с размером в КБ, МБ или ГБ, и время создания/последнего изменения для каждого файла, чтобы точно соответствовать исходному файлу. Таким образом, у меня будет дубликат папки нулевого размера, который я могу заархивировать, но который содержит всю необходимую информацию для файлов в этом каталоге, показывая размер каждого и точное время создания/последнего изменения.

Существуют ли хорошие/простые способы перебора дерева в PowerShell и создания для каждого элемента файла нулевого размера со всей соответствующей информацией, подобной этой?

Используйте Get-ChildItem, чтобы получить пути к файлам в папке, и передайте их в Create-Item с небольшим дополнительным кодом, чтобы сформировать имя файла, используя переданное имя. Если вам нужно поместить информацию о размере в имя файла, вы можете используйте Get-Item на каждом пути, чтобы получить эту информацию, и, возможно, вам придется выполнить некоторые вычисления, чтобы преобразовать ее в ГБ/МБ/Б.

Todd 22.04.2022 22:39

Я не могу найти командлет с именем create-item. Я понимаю, о чем вы говорите get-item Я понимаю, что вы имеете в виду, я могу выбрать значения и, конечно же, я могу выполнить вычисления, чтобы преобразовать размеры в удобочитаемые. Как вы предлагаете создать элемент с правильным временем создания/последней записи (я знаю, что вы говорите Get-Item, но будет ли это тогда Set-Item против моего нового файла нулевого размера?).

YorSubs 22.04.2022 22:56

Извините, я имел в виду New-Item.

Todd 22.04.2022 23:17

Вы можете изменить свойство Attributes объектов, возвращаемых командами Get-Item/Get-ChildItem, чтобы установить атрибуты файла. Посмотрите, поможет ли Эта статья.

Todd 22.04.2022 23:29
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
4
82
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Это был бы один из способов реализовать команду копирования, используя подход, который я упоминал в комментариях. Это должно дать вам возможность черпать идеи. Я не собирался тратить на это столько времени, сколько потратил, но я прогнал его по нескольким каталогам, обнаружил некоторые проблемы и отладил каждую из них, с которой столкнулся. Это довольно солидный пример на данный момент.

function Copy-FolderZeroSizeFiles {
    [CmdletBinding()]
    param( [Parameter(Mandatory)] [string] $FolderPath, 
           [Parameter(Mandatory)] [string] $DestinationPath )

    $dest = New-Item $DestinationPath -Type Directory -Force

    Push-Location -LiteralPath $FolderPath
    try {
        foreach ($item in Get-ChildItem '.' -Recurse) {
                
            $relPath = Resolve-Path -LiteralPath $item -Relative

            $type = if ($item.Attributes -match 'Directory')
                         { 'Directory' }
                    else { 'File'      }

            $destItem = New-Item "$dest\$relPath" -Type $type -Force

            $destItem.Attributes    = $item.Attributes
            $destItem.LastWriteTime = $item.LastWriteTime
        }
    } finally {
        Pop-Location
    }
}

Примечание: приведенная выше реализация является упрощенной и представляет все, что не является каталогом, в виде файла. Это означает символические ссылки и др. будут файлы без информации, с чем они будут связаны.

Вот функция для преобразования количества байтов в формат N.N B/K/M/G. Чтобы получить больше знаков после запятой, просто добавьте 0 в конец строки формата.

function ConvertTo-FriendlySize($NumBytes) {
    switch ($NumBytes) {
        {$_ -lt 1024}       { "{0,7:0.0}B" -f ($NumBytes)             ; break }
        {$_ -lt 1048576}    { "{0,7:0.0}K" -f ($NumBytes / 1024)      ; break }
        {$_ -lt 1073741824} { "{0,7:0.0}M" -f ($NumBytes / 1048576)   ; break }
        default             { "{0,7:0.0}G" -f ($NumBytes / 1073741824); break }
    }
}

Часто люди неправильно понимают эти преобразования. Например, распространенной ошибкой является использование 1024 * 1000 для получения мегабайт (что смешивает значение base10 для 1K со значением base2 для 1K) и следование той же логике для получения ГБ и ТБ.

Спасибо, Тодд, я бы не подумал использовать Resolve-Path -Relative или 'Directory' in $item.Attributes (на самом деле это может не сработать, я изменил его на ($item.Attributes -match 'Directory'), и он работает. Get-ChildItem $FolderPath также вызывает ошибки, я изменил это на $FolderPath.FullName, и это работает. Я точно понимаю расчеты base2, но вы правы, люди часто этого не знают.Буду продолжать работать над этим, постараюсь изложить то, что я придумал ниже.

YorSubs 23.04.2022 08:55

Я думаю, вы правы насчет -match 'Directory'. Я обновил его. Кроме того, я передавал путь к строке в качестве параметра функции. Я думаю, вы разработали свою версию так, чтобы она принимала возвращаемое значение Get-Item, и это нормально. Я только что добавил типизацию к параметрам. @YorSubs

Todd 23.04.2022 13:52

Вот что я придумал с дополнительными частями в вопросе, измените $src / $dst по мере необходимости (D:\VMs я храню много виртуальных машин). Я включил настройку всех параметров CreationTime, LastWriteTime, LastAccessTime, чтобы расположение резервной копии с файлами нулевого размера было идеальным представлением источника. Поскольку я хочу использовать это для архивных целей, я, наконец, заархивировал все и включил отметку даты и времени в имя zip-файла.

# Copy-FolderZeroSizeFiles
$src = "D:\VMs"
$dst = "D:\VMs-Backup"

function ConvertTo-FriendlySize($NumBytes) {
    switch ($NumBytes) {
        {$_ -lt 1024}       { "{0:0.0}B" -f ($NumBytes)             ; break }   # Change {0: to {0,7: to align to 7 characters
        {$_ -lt 1048576}    { "{0:0.0}K" -f ($NumBytes / 1024)      ; break }  
        {$_ -lt 1073741824} { "{0:0.0}M" -f ($NumBytes / 1048576)   ; break }  
        default             { "{0:0.0}G" -f ($NumBytes / 1073741824); break }  
    }
}

function Copy-FolderZeroSizeFiles($FolderPath, $DestinationPath) {
    
    Push-Location $FolderPath
    if (!(Test-Path $DestinationPath)) { New-Item $DestinationPath -Type Directory }

    foreach ($item in Get-ChildItem $FolderPath -Recurse -Force) {
            
        $relPath = Resolve-Path $item.FullName -Relative

        if ($item.Attributes -match 'Directory') {
            $new = New-Item "$DestinationPath\$relPath" -ItemType Directory -Force -EA Silent
        } 
        else {
                $fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($item.Name)
                $fileExt      = [System.IO.Path]::GetExtension($item.Name)
                $fileSize     = ConvertTo-FriendlySize($item.Length)
                $new = New-Item "$DestinationPath\$(Split-Path $relPath)\$fileBaseName ($fileSize)$fileExt" -ItemType File
        }
        "$($new.Name) : creation $($item.CreationTime), lastwrite $($item.CreationTime), lastaccess $($item.LastAccessTime)"
        $new.CreationTime = $item.CreationTime
        $new.LastWriteTime = $item.LastWriteTime
        $new.LastAccessTime = $item.LastAccessTime
        $new.Attributes = $item.Attributes   # Must this after setting creation/write/access times or get error on Read-Only files
    }
    Pop-Location
}

Copy-FolderZeroSizeFiles $src $dst

$dateTime = Get-Date -Format "yyyyMMdd_HHmm"
$zipName = "$([System.IO.Path]::GetPathRoot($dst))\$([System.IO.Path]::GetFileName($dst))_$dateTime.zip"

Add-Type -AssemblyName System.IO.Compression.FileSystem
[IO.Compression.ZipFile]::CreateFromDirectory($dst, $zipName)

@Todd, поскольку это сканирование дерева папок, я знаю, что это может быть медленным для большого набора файлов, если я выполняю слишком много операций. Вы видите какую-либо неэффективность в том, как я добавил размер файла к новому имени, то есть, может быть, split-path неэффективно, или, может быть, выполнение трех операций для каждого из времени создания, времени последней записи, времени последнего доступа может быть выполнено в одной команде и т. д.?

YorSubs 23.04.2022 15:28

Это похоже на алгоритм O(n), поэтому он довольно эффективен. Вы не копируете содержимое файла - в основном только имена - поэтому он должен просто быстро выполнить операцию. На самом деле ничто не кажется мне явно неэффективным. Отличная работа.

Todd 23.04.2022 15:42

После того, как я немного отладил свой пример, я обнаружил, что мы должны использовать опцию -LiteralPath для Resolve-Path, иначе это может привести к сбою в именах файлов с квадратными скобками в них. Я также понял, что ссылки представлены в виде файлов в папке dest, что, на мой взгляд, хорошо, но стоит упомянуть. Кроме того, New-Item с параметром -Force создаст новую папку, если она не существует, и молча выдаст ошибку, если она существует. Это означает, что вам не нужно проверять, существует ли папка перед вызовом.

Todd 23.04.2022 22:44

Другие вопросы по теме