又到了每月一次的PowerShell脚本比赛。这次的题目很有趣,测试了好几个知识点,豆子花了半天的功夫才全部实现。
http://powershell.org/wp/2016/03/05/2016-march-scripting-games-puzzle/
题目如下:
某欧洲文件服务器里面有大量的文件名是使用拉丁字母命名的,把他们都找出来。
具体的要求有以下几点:
所有的这些拉丁字母(法语,德语等等)都是属于Latin-1 字母范畴,只需要找到包含这些字母的文件,其他的拉丁符号不用考虑。
写一个函数来获取这些文件的名称,位置,尺寸;尺寸需要好的可读性,比如小文件显示多少K,大文件显示多少M或者多少G
如果找到了对应的文件信息,按下列格式yyyyMMdd_FileNamesWithDiacritics.csv 保存为csv文件。yyyyMMdd表示年份,月份和日期。
写一个计划任务,每2周六晚上11点执行上面的函数
把上面生成的附件发邮件给管理员
http://powershell.org/wp/wp-content/uploads/2016/03/FileShare.zip 这个是用来测试的文件
下面是豆子完成的步骤:
1.首先需要解决的问题是怎么找到这些拉丁字母?根据提示,我发现Latin-1的Unicode代码如下所示。如果只是显示字母而不包括其他的符号,那么他的代码范围是00C0到00FF
那这样的话 我可以通过正则表达式来进行判断是否文件名包括了这些符号。比如
1
|
Get-ChildItem
-Recurse c:\test |
Where-Object
{
$_
.name
-match
"[\u00C0-\u00FF]"
}
|
2. 输出文件大小,还必须有很好的可读性。他默认的输出结果是按字节排列的,我需要根据大小进行重新定义,如果精度太长也很难看,我需要保留小数点后一位就行了。
我可以在自定义的字段里面进行判断,如果小于1000字节的 用Byte显示,小于1M 的用KB显示,大于1G的用MB显示。
例如
1
2
3
4
5
6
7
8
9
10
11
|
Get-ChildItem
-Recurse -Path
$path
|
Where-Object
{
$_
.name
-match
"[\u00C0-\u00FF]"
} |
select Name, directory, creationtime, lastwritetime,
@{
n=
"Size"
;
e={
if
(
$_
.length
-lt
1000){
"{0:n1}"
-f
$_
.length.tostring()+
" Byte"
}
elseif
(
$_
.length
-lt
1000000){(
"{0:n1}"
-f
(
$_
.length/1kb)).ToString()+
" KB"
}
else
{(
"{0:n1}"
-f
(
$_
.length/1mb)).ToString() +
" MB"
}
}
} | tee -Variable file
|
3. 按照时间格式保存,可以使用 get-date -Format YYYY.M.d 来实现。注意我export-csv的时候指定了编码格式是Unicode,不然默认的是ASII格式只会显示问号。
1
2
3
4
5
|
if
(
$file
-eq
$null
){
Write-Warning
"No file name dectected with Latin Character"
}
else
{
$name
=(
get-date
-Format yyyy.M.d)+
"FileNamesWithDiacritics.csv"
$file
|
export-csv
c:\temp\
$name
-Encoding Unicode}
|
4.计划任务。这里应该有个bug。我用的是Windows10和Powershell 5,但是当我创建触发器的时候会报错找不到对应的命令。经过研究,需要手动注册对应的mof文件的内容到WMI库里面。
mofcomp那条命令就是手动注册的命令。
1
2
3
4
|
mofcomp C:\Windows\System32\wbem\SchedProv.mof
$action
=
New-ScheduledTaskAction
-Execute
'Powershell.exe'
-Argument
'Get-Diacritic.ps1 '
$trigger
=
New-ScheduledTaskTrigger
-Weekly -WeeksInterval 2 -DaysOfWeek Saturday -At 3am
Register-ScheduledTask
-Action
$action
-Trigger
$trigger
-TaskName
"LatinName"
-Description
"Weekly FileName Scanning"
|
5.发送文件给管理员
注意这里我用的是Office365测试的,所以端口是587。我为了省事,密码用的是明文,比较好的做法应该是把加密之后的指纹(一堆乱码),拷贝到脚本里面使用。
1
2
3
4
5
6
7
8
9
|
$from
=
"abc@test.com"
$to
=
"abc@test.com"
$smtp
=
"smtp.office365.com"
$sub
=
"file list"
$body
=
"Attached is the file list"
$attach
=
"C:\scripts\file.csv"
$secpasswd
=
ConvertTo-SecureString
"Password"
-AsPlainText -Force
$mycreds
=
New-Object
System.Management.Automation.PSCredential (
$from
,
$secpasswd
)
Send-MailMessage
-To
$to
-From
$from
-Subject
$sub
-Body
$body
-Credential
$mycreds
-SmtpServer
$smtp
-DeliveryNotificationOption Never -BodyAsHtml -UseSsl -port 587 -Attachments
$attach
|
最后给个完整的版本
Get-Diacritic.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
function
Get-Diacritic
{
[
CmdletBinding
()]
Param
(
# Param1 help description
[
Parameter
(
ValueFromPipelineByPropertyName
=
$true
,
Position
=0)]
$Path
=
".\"
)
Begin
{
}
Process
{
Get-ChildItem
-Recurse -Path
$path
|
Where-Object
{
$_
.name
-match
"[\u00C0-\u00FF]"
} |
select Name, directory, creationtime, lastwritetime,
@{
n=
"Size"
;
e={
if
(
$_
.length
-lt
1000){
"{0:n1}"
-f
$_
.length.tostring()+
" Byte"
}
elseif
(
$_
.length
-lt
1000000){(
"{0:n1}"
-f
(
$_
.length/1kb)).ToString()+
" KB"
}
else
{(
"{0:n1}"
-f
(
$_
.length/1mb)).ToString() +
" MB"
}
}
} | tee -Variable file
if
(
$file
-eq
$null
){
Write-Warning
"No file name dectected with Latin Character"
}
else
{
$name
=(
get-date
-Format yyyy.M.d)+
"FileNamesWithDiacritics.csv"
$file
|
export-csv
c:\temp\
$name
-Encoding Unicode}
$from
=
"abc@test.com"
$to
=
"def@test.com"
$smtp
=
"smtp.office365.com"
$sub
=
"file list"
$Body
=
$file
|
ConvertTo-Html
-Head
"Scanning Result"
-As
Table |
Out-String
$attach
=
"c:\temp\"
+
$name
$secpasswd
=
ConvertTo-SecureString
"Password"
-AsPlainText -Force
$mycreds
=
New-Object
System.Management.Automation.PSCredential (
$from
,
$secpasswd
)
Send-MailMessage
-To
$to
-From
$from
-Subject
$sub
-Body
$body
-Credential
$mycreds
-SmtpServer
$smtp
-DeliveryNotificationOption Never -BodyAsHtml -UseSsl -port 587 -Attachments
$attach
}
End
{
}
}
Get-Diacritic
c:\users\yli\Downloads
|
计划任务脚本
1
2
3
4
|
mofcomp C:\Windows\System32\wbem\SchedProv.mof
$action
=
New-ScheduledTaskAction
-Execute
'Powershell.exe'
-Argument
'Get-Diacritic -path C:\users\yli\Downloads'
$trigger
=
New-ScheduledTaskTrigger
-Weekly -WeeksInterval 2 -DaysOfWeek Saturday -At 3am
Register-ScheduledTask
-Action
$action
-Trigger
$trigger
-TaskName
"LatinName"
-Description
"Weekly FileName Scanning"
|
运行结果
下面的值保存为CSV文件
保存的文件名
创建的计划任务
执行一下计划任务
1
|
Start-ScheduledTask
-TaskName
"LatinName"
|
收到的邮件