отправить электронное письмо только с одной строкой резюме robocopy

Может ли кто-нибудь помочь мне сделать так, чтобы отображалась только одна строка журнала электронной почты? что-то вроде этого:

Bytes Copied: 27.0 k on Thursday, April 29, 2021 6:15:20 PM

Этот код дает мне этот журнал:

$Logfile = "C:\Powershell\robocopy.txt"
Clear-Content "C:\Powershell\robocopy.txt" -Force
$EmailFrom = "[email protected]"
$EmailTo = "[email protected]"
$EmailBody = "completed successfully. See attached log file"
$EmailSubject = "Summary"

$files = @("SCRIPT")

for($i = 0; $i -lt $files.Count; $i++){
    robocopy "C:\$($files[$i])" "C:\NEW TEST\folder\folder\$($files[$i])" /Z /e /xx /W:5 /MAXAGE:2 /NFL /NDL /NJH /nc /np /unilog+:$Logfile
}

Send-MailMessage -To $EmailTo -from $EmailFrom -Subject $EmailSubject -Body $EmailBody -attachment $Logfile -smtpserver 192.168.24 -Port 25


#log give me this:
------------------------------------------------------------------------------
               Total    Copied   Skipped  Mismatch    FAILED    Extras
    Dirs :         4         0         4         0         0         0
   Files :        17         0        17         0         0         0
   Bytes :    27.0 k         0    27.0 k         0         0         0
   Times :   0:00:00   0:00:00                       0:00:00   0:00:00
   Ended : Thursday, April 29, 2021 11:55:52 AM

person tricky69    schedule 30.04.2021    source источник
comment
Откуда вы взяли 493g?   -  person Mathias R. Jessen    schedule 30.04.2021
comment
хотел набрать 27.0k извините   -  person tricky69    schedule 30.04.2021


Ответы (2)


Итак, предполагая, что журнал — это просто сводные данные, вы можете довольно эффективно его анализировать:

$Log  = Get-Content C:\temp\robocopy.txt
$Date = ($Log[-1] -split ":", 2)[1].Trim()
$Line = $log | Where-Object{$_ -match "^\s+Bytes.+"}
$Line = $Line -split "\s+"
$Line = "Bytes Copied: {0} {1} on {2}" -f $Line[3], $Line[4], $Date

$Line

Это должно вывести:

Bytes Copied: 27.0 k on Thursday, April 29, 2021 11:55:52 AM

Его можно вставить после запуска команды RoboCopy. Затем укажите $Line в тексте или теме письма...

Я не проверял ваш журнал RoboCopy, чтобы убедиться, что формат соответствует продемонстрированному. Тем не менее концепции, изображенные здесь, должны хорошо служить цели.

Объяснение:

  • Получение даты простым просмотром последней строки в массиве, возвращаемом Get-Content. Однако это можно сделать с помощью аналогичной стратегии сопоставления, если, например, подход [-1] ненадежен.
  • Чтобы определить количество байтов в нужной строке. сопоставьте строку, затем разделите ее и обратитесь к элементам, которые, как мы знаем, содержат данные.
  • Наконец, используйте 2 извлеченные строки для форматирования нужной строки.

Примечание. Как объединение, так и расширение строк также являются вариантами окончательной сборки линии. Тем не менее, я думал, что подход -f был более удобочитаемым для этой конкретной работы.

Примечание. При этом сохраняется любой используемый RoboCopy символ множителя, т. е. k, m или любой другой. Потребовалось бы больше работы и логики, чтобы вычислить и отобразить по-другому.

Примечание. Само собой разумеется, что существует миллион способов сделать это. Если я что-то улучшу, я постараюсь добавить больше примеров.

Другой подход:

Немного иначе, используйте оператор -match как для даты, так и для количества данных:

$Log  = Get-Content C:\temp\robocopy.txt
$Date = (($log -match "^\s+Ended.+") -split ":", 2)[1].Trim()
$Line = $Log -match "^\s+Bytes.+"
$Line = $Line -split "\s+"
$Line = "Bytes Copied: {0} {1} on {2}" -f $Line[3], $Line[4], $Date

$Line

Это будет работать до тех пор, пока $Log является массивом, потому что -match будет возвращать совпадения напрямую. Таким образом, нам не нужно предложение Where-Object.

Обновление:

Мне приходит в голову, что, поскольку вы запускаете несколько заданий RoboCopy в цикле и используете параметр \Unilog+, вы, вероятно, сталкиваетесь не только с анализом данных и количества данных, но и с несколькими запусками и/или с несколькими такими сегментами в файл журнала. Просто хочу решить эту проблему, поскольку у вас есть несколько вариантов.

  1. Поскольку вы выполняете RoboCopy в цикле, а число $LogFile известно и постоянно в этом цикле, вы можете добавить разделительную строку в конце каждого задания.
for($i = 0; $i -lt $files.Count; $i++){
    $Log = robocopy "C:\$($files[$i])" "C:\NEW TEST\folder\folder\$($files[$i])" /Z /e /xx /W:5 /MAXAGE:2 /NFL /NDL /NJH /nc /np /unilog+:$Logfile /tee
    "---Job Completed---" | Add-Content $Logfile
}

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

  1. Добавьте параметр /TEE в свою команду RoboCopy и назначьте вывод непосредственно переменной. Это я продемонстрирую:
    $Logfile = "C:\Powershell\robocopy.txt"
    Clear-Content "C:\Powershell\robocopy.txt" -Force
    $EmailFrom = "[email protected]"
    $EmailTo = "[email protected]"
    $EmailBody = [Collections.ArrayList]@("completed successfully. See attached log file & below summary","")
    $EmailSubject = "Summary"
    
    $files = @("SCRIPT")
    
    for($i = 0; $i -lt $files.Count; $i++){
        $Log = robocopy "C:\$($files[$i])" "C:\NEW TEST\folder\folder\$($files[$i])" /Z /e /xx /W:5 /MAXAGE:2 /NFL /NDL /NJH /nc /np /unilog+:$Logfile /tee
    
        $Date = (($log -match "^\s+Ended.+") -split ":", 2)[1].Trim()
        $Line = $Log -match "^\s+Bytes.+"
        $Line = $Line -split "\s+"
        $Line = "Bytes Copied: {0} {1} on {2}" -f $Line[3], $Line[4], $Date
    
        [Void]$EmailBody.Add($Line)
    }
    
    #Flip the $EmailBody array back to being a regular string.
    $EmailBody = $EmailBody -join "`r`n"
    
    Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -Body $EmailBody -Attachment $Logfile -SMTPserver 192.168.24.55

Вы можете видеть, что $Log теперь захватывает выходные данные RoboCopy напрямую, и вы можете выполнить оставшуюся часть кода, которая выглядит аналогично, что избавляет нас от необходимости пост-обработки журнала, как я упоминал в Вариантах 1. Переключение $EmailBody на список массивов включено нам легко добавлять рассчитанные строки в массив. Преобразуйте массив обратно в строку, чтобы он подходил для параметра -EmailBody в командлете Send-MailMessage.

Обновление для комментария:

Вы можете изменить отображение скопированных данных, а не всего, просто переместив индекс массива, на который мы ссылаемся:

$Log  = Get-Content C:\temp\robocopy.txt
$Date = (($log -match "^\s+Ended.+") -split ":", 2)[1].Trim()
$Line = $Log -match "^\s+Bytes.+"
$Line = $Line -split "\s+"
$Line = "Bytes Copied: {0} {1} on {2}" -f $Line[5], $Line[4], $Date

$Line

ОДНАКО обратите внимание, что я все еще ссылаюсь на $Line[4]. Это потому, что RoboCopy не добавляет символ множителя, такой как k, когда есть 0 байтов, поэтому мы могли бы также взять символ, который соответствует сумме. Тем не менее, если сумма также равна 0, это может сбросить индексы массива, мы, конечно, можем приспособить это к дальнейшей работе, но вам придется решить вероятность этого сценария.

Примечание. Я еще не изменил более ранние примеры. Когда QA будет решен, я постараюсь перефразировать, чтобы он был последовательным. Учитывая, что было несколько обновлений.

Обновление для комментария, запрашивающего экспорт Csv

Командлеты *-Csv преобразуют объекты в значения, разделенные запятыми (строки), используя свойства объекта в качестве заголовков столбцов и записывая их в файл. Таким образом, последующий пример, который предусматривает требование предыдущего тела электронной почты и экспорт Csv, может выглядеть примерно так:

$Logfile = "C:\PowerShell\robocopy.txt"
$CsvFile = "C:\PowerShell\RoboCsv.txt"
Clear-Content "C:\Powershell\robocopy.txt" -Force
$EmailFrom = "[email protected]"
$EmailTo = "[email protected]"
$EmailBody = [Collections.ArrayList]@("completed successfully. See attached log file & below summary","")
$EmailSubject = "Summary"
$CsvData      = [Collections.ArrayList]@()

$files = @("SCRIPT")

for($i = 0; $i -lt $files.Count; $i++){
    $Source = "C:\$($files[$i])"
    $Dest   = "C:\NEW TEST\folder\folder\$($files[$i])"
    
    $Log = robocopy $Source $Dest /Z /e /xx /W:5 /MAXAGE:2 /NFL /NDL /NJH /nc /np /unilog+:$Logfile /tee

    $Date = (($log -match "^\s+Ended.+") -split ":", 2)[1].Trim()
    $Line = $Log -match "^\s+Bytes.+"
    $Line = $Line -split "\s+"
    
    $Copy = $Line[5]
    $Mult = $Line[4]

    $Line = "Bytes Copied: $Copy $Mult on $Date"

    [Void]$EmailBody.Add($Line)

    # For Csv output:
    [Void]$CsvData.Add(
        [PSCustomObject]@{
            SizeCopied  = $Copy
            Date        = $Date
            Source      = $Source
            Destination = $Dest
        } )
}

#Flip the $EmailBody array back to being a regular string.
$EmailBody = $EmailBody -join "`r`n"

Send-MailMessage -To $EmailTo -From $EmailFrom -Subject $EmailSubject -Body $EmailBody -Attachment $Logfile -SMTPserver 192.168.24.55

#Output to CSVFile
$CsvData | Export-Csv -Path $CsvFile -NoTypeInformation

Что добавлено, так это массив пользовательских объектов, использующих значения, которые мы вычислили ранее. Очевидно, были определены некоторые новые переменные, потому что нам нужно повторно использовать/ссылаться, чтобы сделать 2 вещи вместо 1.

Обратите внимание, если я делал это с нуля и лучше разбирался в требованиях. Я мог бы структурировать это по-другому. Несколько вещей, которые приходят на ум:

  1. Используйте другую конструкцию цикла. Не похоже, что традиционный цикл технически необходим, и код может быть более читабельным с одной из конструкций ForEach.
  2. Заполнять только массив объектов в цикле. Затем выполните постобработку массива, чтобы извлечь необходимые данные тела электронной почты, а также команду Export-Csv. Это также может позволить вам полностью избежать проблемы с увеличением массива и, следовательно, не беспокоиться о [Collections.ArrayList].

Кроме того:

  • Вам не нужно указывать -Port 25 по умолчанию.
  • Вам не хватает последнего октета IP-адреса вашего SMTP-сервера, для которого я только что добавил 55, но вы захотите это исправить.
person Steven    schedule 30.04.2021
comment
Может ли он показать, что скопировано вместо общего количества байтов? например, показать второй столбец, где написано «скопировано», а не «всего»? спасибо! - person tricky69; 30.04.2021
comment
Я знал, что этот вопрос будет, я просто добавил еще одно обновление, чтобы решить его. - person Steven; 30.04.2021
comment
Привет, извини, что так много прошу. Но может ли это работать с файлом .csv? Нравится столбец для размера копии, даты, источника и места назначения? Это очень помогло бы мне. Я ценю ваше время в помощи мне! - person tricky69; 30.04.2021
comment
Вы хотите вывести в csv? - person Steven; 30.04.2021
comment
да если это возможно. Я действительно ценю твою помощь. - person tricky69; 30.04.2021
comment
ОК, я добавил что-то работоспособное. Примечание. Я не проверял это, но этого должно быть достаточно, чтобы вы туда попали. Если вы не возражаете, пожалуйста, примите ответ, это поможет другим найти его. Более того, мы выходим за рамки исходного вопроса. - person Steven; 30.04.2021
comment
Я получаю эти ошибки :( Вы не можете вызвать метод для выражения с нулевым значением. В строке: 29 char: 5 + $Date = (($log -match ^\s+Ended.*) -Split:, 2)[ 1].Trim() + CategoryInfo: InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId: InvokeMethodOnNull Вы не можете вызвать метод для выражения с нулевым значением. В строке: 41 char: 5 + [Void] $CsvExportFile.Add( + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull - person tricky69; 30.04.2021
comment
Я сейчас не за компом. Однако это, вероятно, исходит из вызова .trim() и будет означать, что более ранние операторы не возвращали ожидаемые данные. - person Steven; 30.04.2021
comment
Я постараюсь разобраться :/ Но если вы тоже посмотрите, было бы неплохо. Я очень ценю, что ты помогаешь мне. - person tricky69; 30.04.2021
comment
Давайте продолжим обсуждение в чате. - person Steven; 30.04.2021

Очевидно, что ответ @Steven намного лучше и глубже моего, но я все равно решил поделиться этим уродливым фрагментом кода:

$Log = "C:\temp\robocopy.txt"
$Bytes = (Get-Content $Log | Where-Object {$_ -like "*bytes*"} | Select -First 1).split(':')[1].trim(' ').split(' ')[0]
$Body = "Bytes Copied: $($Bytes)k on $(Get-Date)"
Send-MailMessage -To $EmailTo -from $EmailFrom -Subject $EmailSubject -Body $Body -attachment $Logfile -smtpserver 192.168.24 -Port 25

Или поместите его в файл для прикрепления. Это может быть уродливый код, но он получает информацию, которая вам нужна.

person Lars Panzerbjrn    schedule 30.04.2021
comment
Спасибо, что поделился. Часто бывает так, что кто-то отвечает, когда вы уже работали над чем-то, поэтому я всегда чувствую, что; тоже можно... Особенно если есть разница в подходе к решению проблемы. - person Steven; 01.05.2021