问题描述
有人可以帮助我做到这一点,以便仅显示电子邮件日志的一行吗?像这样的东西:
Can someone help me make it so that only one line of the email log is shown? something like this:
Bytes Copied: 27.0 k on Thursday, April 29, 2021 6:15:20 PM
这段代码给我这个日志:
This code give me this log:
$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
推荐答案
因此,假设日志只是摘要数据,则可以非常有效地对其进行解析:
So assuming the log is just the summary data you can parse it pretty effectively:
$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
...
It can be inserted after you run your RoboCopy command. Then reference $Line
in your email body or subject...
我没有测试您的RoboCopy日志记录以确保格式如所示.不过,此处描述的概念应该可以很好地达到目的.
I didn't test your RoboCopy logging to ensure the format is as demonstrated. Nevertheless the concepts depicted here should serve the purpose well.
说明:
- 通过简单地查看由
Get-Content
返回的数组的最后一行来获取日期.但是,例如,如果[-1]
方法不可靠,则可以使用类似的匹配策略来完成. - 计算所需行的字节计数部分.匹配线,然后将其拆分并引用我们知道可容纳数据的元素.
- 最后使用提取的2个字符串来格式化所需的字符串.
- Getting the date by simply looking at the last line in the array returned by
Get-Content
. However, this could be done using a similar match strategy if for example, the[-1]
approach isn't reliable. - To work out the byte count portion of the desired line. match the line, then split it up and refer to the elements we know hold the data.
- Finally use the 2 extracted strings to format the desired string.
注意:连接字符串和扩展字符串也是该行最终组装的选项.但是,我认为 -f
方法对于此特定工作更具可读性.
Note: Both concatenation and expanding strings are also options for the final assembly of the line. However, I thought the -f
approach was more readable for this particular job.
注意:这将保留使用的RoboCopy乘数,即k,m或其他任何符号.要进行不同的计算和显示,将需要更多的工作和逻辑.
Note: This keeps whatever multiplier symbol RoboCopy used, i.e. k, m or whatever. It would take more work and logic to calculate and display differently.
注意:几乎可以说有大约一百万种方法可以做到这一点.如果我精打细算,我会尝试添加更多示例.
Note: It almost goes without saying there are probably a million ways to do this. If I refine at all I'll try to add more examples.
另一种方法:
稍有不同,请对日期和数据量使用 -match
运算符:
Slightly different, use the -match
Operator for both the date and the data quantity:
$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
子句.
This will work as long as $Log
is an array, because -match
will return the matches directly. As such we don't need the Where-Object
clause.
更新:
在我看来,由于您正在循环运行多个RoboCopy作业,并使用 \ Unilog +
参数,因此您可能不仅要解析数据和数据量,而且还要处理因此跨多个运行和/或在日志文件中包含多个此类段.只是想解决这个问题,因为您有几个选择.
It occurs to me that since you are running multiple RoboCopy jobs in a loop, and using the \Unilog+
parameter, you're probably faced with not only parsing out the data and data quantity but doing so across multiple runs and or with multiple such segments in the log file. Just want to address that as you have a few options.
- 由于您正在循环中执行RoboCopy,并且在该循环中
$ LogFile
是已知的并且是常量,因此您可以在每个作业的结尾处添加分隔线.
- Since you're executing RoboCopy in loop and the
$LogFile
is known and constant with in that loop, you can add a separator line at the conclusion of each job.
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
}
我将不进行演示,因为有一种更方便的方法,但这将为您提供一个已知的字符串,以便将日志文件后处理为所需的字符串.
I'm not going to demonstrate it, because there's amore convenient way, but this would give you a known string to split on in order to post-process the log file down to the strings you need.
- 在您的RoboCopy命令中添加
/TEE
参数,并将输出直接分配给变量.我将演示这一点:
- Add the
/TEE
parameter to your RoboCopy command and assign the output directly to a variable. This one I will demonstrate:
$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
切换到数组列表使我们能够轻松地将计算出的行附加到数组中.将数组转换回字符串,以便适用于 Send-MailMessage
cmdlet中的 -EmailBody
参数.
You can see $Log
now captures the RoboCopy output directly, and you can execute the the remainder of the code looks similar, which saves us the need to post-process the log, as I mentioned in Options 1. Switching $EmailBody
to an array list enabled us to easily append out calculated lines to an array. Convert the array back to a string so it's suitable for the -EmailBody
parameter in the Send-MailMessage
cmdlet.
更新以发表评论:
您可以通过简单地推进我们所引用的数组索引来更改以显示复制的数据而不是总数:
You can change to show copied data rather than the total by simply advancing the array index we're referencing:
$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,则可能会舍弃数组索引,我们当然可以通过进一步的工作来容纳它,但是您必须确定这种情况的可能性.
HOWEVER, notice that I'm still referencing $Line[4]
. That's because RoboCopy isn't appending a multiplier symbol like "k" when there's 0 bytes, so we might as well take the symbol that goes with the total. That said, if the total is also 0, it may throw off the array indices, we can of course accommodate it with further work, but you'd have to decide the likelihood of that scenario.
注意:我尚未更改较早的示例.质量检查解决后,我将尝试重新措词,以确保其前后一致.考虑到有很多更新.
Note: I haven't yet changed earlier examples. When the QA is settled, I'll try to reword so it's consistent. Considering there've been a number of updates.
更新注释以请求Csv导出
*-Csv cmdlet使用对象属性作为列标题将对象转换为逗号分隔的值(字符串),并将其写入文件.因此,提供先前电子邮件正文要求并进行Csv导出的后续示例可能类似于:
The *-Csv cmdlets convert objects to comma separated values (strings), using the object properties as column headers and write that to file. So a subsequent example that provides for previous email body requirement and does the Csv export might look something like:
$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.
What's added is an array of custom objects using values we previously calculated. Obviously some new variables were defined because we have to reuse/reference to do 2 things instead of 1.
请注意,如果我是从头开始的,并且对要求有更好的了解.我的结构可能会有所不同.我想到了几件事:
Please note if I were doing this from scratch and had a better handle on the requirements. I might structure it differently. A few things that come to mind:
- 使用其他循环构造.从技术上看,它似乎似乎不需要传统的循环,并且使用ForEach构造之一可以使代码更具可读性.
- 仅在循环中填充对象数组.然后对数组进行后期处理,以提取所需的电子邮件正文数据以及
Export-Csv
命令.这也可以使您完全避免数组增加的问题,从而不必担心[Collections.ArrayList]
.
- Use a different loop construct. It doesn't look like the traditional loop is technically needed, and the code may be more readable with one of the ForEach constructs.
- Only populate the the object array in the loop. Then post process the array to extract the email body data you need as well as the
Export-Csv
command. This may also allow you to completely avoid the array incrementation issue and thus not bother with[Collections.ArrayList]
.
一旁:
- 您无需指定默认的
-Port 25
. - 您丢失了SMTP服务器IP地址的最后一个八位位组,我刚刚将其输入"55",但是您需要对此进行修复.
这篇关于仅发送一行robocopy摘要的电子邮件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!