【问题标题】:Monitor and set an alert for Invalid file names监控无效文件名并设置警报
【发布时间】:2015-08-04 05:37:11
【问题描述】:

我想监控人们为某些活动放置 Excel 工作表的特定文件夹。

我想确保人们在放置 Excel 表之前应该注意以下规则。

  1. Excel 工作表应为 xls 格式(Excel 97-2003 Workboomk 格式)
  2. excel文件名不能包含空格
  3. 文件名的长度不应超过 15 个字符。

如果用户在不遵守上述 3 条规则的情况下放置了 Excel 表格,我想给我设置电子邮件提醒。

我需要一个 VB 或 Powershell 脚本或批处理来监控文件夹。请帮我解决这个问题。

【问题讨论】:

  • 在此处查看有关 Powershell 中的 FileSystemWatcher 的大量文章。您可以在特定文件夹中查找所有内容,然后编写一个包含验证规则的函数。如有必要,我很乐意发布更详细的示例。告诉我。
  • 感谢您的建议。我对脚本编写很陌生。可以发个例子。这样我就可以根据您的示例构建我的脚本。
  • 假设你有一个sheet1.xls,但里面有一个可执行文件。需要查询这个条件吗?
  • @Vesper,不,用户只放置 Excel 表格。我需要 excel 应该是 xls 格式,但是一些用户错误地将 excel 表放置在 xlsx 格式。后端有工作仅在 xls 格式时才使用 excel 表。因此,如果有任何格式错误的 Excel 工作表、名称中有空格并且 Excel 工作表名称的长度超过某个特定限制,则作业将无法运行。人们正在放置大约 1000 个 excel 表,如果作业没有运行,我无法手动监控每个表
  • 使用File Screening 可能比编写自定义代码获得更多。通过这种方式,用户将快速适应强制执行的规则。

标签: powershell batch-file vbscript


【解决方案1】:

这听起来像两个步骤。第 1 步,编写一段可运行的代码以检查文件的条件是否正确。第 2 步,通过电子邮件向用户发送错误命名文件的列表。第 1 步相当简单。

Dim fso, scanFolder, files, n, fileList, fileArray
Set fso = CreateObject("Scripting.FileSystemObject")
Set scanFolder = fso.GetFolder("C:\Your\Folder\Here")
Set files = scanFolder.Files
fileList = ""

for each n in files
    if len(n.name) > 15 or right(n.name, 3) <> "xls" or InStr(n.name, " ") > 0 then
        fileList = fileList & n.name & "|"
    end if
next

fileList = left(fileList, len(fileList) - 1)
fileArray = split(fileList, "|")

这将获取所有命名不当文件的列表。

第 2 步。不幸的是,据我所知,FSO 无法从文件中获取所有者名称。您可以做的最好的事情是通过电子邮件发送一个分发列表,其中包含错误命名的文件列表。我希望这会有所帮助。

编辑:这是我在脚本中使用的电子邮件代码。

Dim objEmail, strFileList, x
strFileList = ""

for each x in fileArray
    strFileList = strFileList & x & vbNewLine
next

Set objEmail = CreateObject("CDO.Message")
with objEmail
    .From = "From@email.com"
    .To = ""
    .CC = ""

    .Subject = "List of wrong files"
    .Body = strFileList

    .Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    .Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = 'you will need to get the code for this from IT'
    .Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    .Configuration.Fields.Update
end with
objEmail.Send

【讨论】:

    【解决方案2】:

    我已将 FileSystemWatcher 的功能封装到一个模块中,并提供了一个小示例,说明如何将其用于您的要求。

    # Module FileSystemWatcher
    
    <#
        .SYNOPSIS
        Register to receive filesystemwatcher events for a file or folder.
    #>
    
    function Register-FileSystemEvent
    {
        [CmdLetBinding(DefaultParameterSetName="File")]
        param
        (
            [Parameter(Mandatory=$true,Position=0,ParameterSetName="File")]
            [string]$path,
            [Parameter(Mandatory=$true,Position=0,ParameterSetName="Folder")]
            [string]$folder,
            [Parameter(Mandatory=$false,Position=1,ParameterSetName="Folder")]
            [string]$filter="*.*",
            [Parameter(Mandatory=$true,Position=2)]
            [string]$event,
            [scriptblock]$action={
                # Action code here
                $sourceidentifier = $Event.SourceIdentifier
                $FileName = $Event.SourceEventArgs.FullPath
                $EventType = $Event.SourceEventArgs.ChangeType
                $EventTime = $Event.TimeGenerated
                $message = ("File {0} {1} at {2:yyyyMMdd HH:mm:ss}" -f $FileName, $EventType, $EventTime)
                Msg ([environment]::UserName) $message
            },
            $MessageData
    
        )
        if ($PSCMdLet.ParameterSetName -eq "File")
        {
            $folder = $path.Replace(([System.IO.Path]::GetFileName($path)), "")
            $filter = [System.IO.Path]::GetFileName($path)
        }
        $watcher = New-Object System.IO.FileSystemWatcher -ArgumentList $folder, $filter
        $uniqueId = ("{0:yyyyMMddHHmmss}" -f [DateTime]::Now)
        $sourceIdentifier = ("File_{0}_{1}" -f $event, $uniqueId)
        $job = Register-ObjectEvent -InputObject $watcher -EventName $event -SourceIdentifier $sourceIdentifier -Action $action -MessageData $MessageData
        $Script:ActiveWatchers += @{$sourceIdentifier = $job}
    }
    
    <#
        .SYNOPSIS
        Unregister a FileSystem event or All
        .PARAMETER All
        Unregisters all events
    #>
    function Unregister-FileSystemEvent
    {
        param
        (
            $id,
            [switch]$All
        )
        if ($All.IsPresent)
        {
            foreach ($item in $Script:ActiveWatchers.Keys)
            {
                Unregister-Event $item
                Remove-Job $item
            }
            $Script:ActiveWatchers = @{}
        }
        else
        {
            Unregister-Event $id
            Remove-Job $id
            $Script:ActiveWatchers.Remove($id)
        }
    
    }
    
    
    function Init
    {
        if ($Script:ActiveWatchers.Count -gt 0){Unregister-FileSystemEvent -All}
        $Script:ActiveWatchers = @{}
    }
    
    Init
    
    Export-ModuleMember -Function Register-FileSystemEvent, Unregister-FileSystemEvent `
        -Variable ActiveWatchers
    

    这个模块的基本思想是能够指定一个脚本块在文件事件被触发后执行。 -MessageData 用于在事件触发后将更多数据附加到事件中。在下面的示例中,它用于发送 smtpserver 和凭据。

    下面的示例代码使用 fileSystemWatcher 模块并在每次创建文件时发送一封包含结果的电子邮件。

    请注意,Send-MailMessage 的参数可能不是您的邮件服务器所必需的,而是我过去使用 office365 服务器发送电子邮件的参数。

    # Demo using FileSystemWatcher to validate created files
    
    Import-Module .\FileSystemWatcher
    $smtpserver = Read-Host -Prompt "Specify your smtp server"
    $mailcred = Get-Credential -Message "Specify credentials for your smtp server"
    
    $eventid = Register-FileSystemEvent -folder C:\users\Jower\OneDrive\Documents -event Created -MessageData @{SmtpServer = $smtpserver;Credentials = $mailcred} -action {
        function ValidateFile($filename)
        {
            $messages = @()
            if ([System.IO.Path]::GetExtension($filename) -ne ".xls")
            {
                $messages += ("{0}: Only excel version 97 files allowed here" -f $filename)
            }
            if ([System.IO.Path]::GetFileName($filename).Contains(" "))
            {
                $messages += ("{0}: Spaces are not allowed in filename" -f $filename)
            }
            if ($filename.Length -gt 15)
            {
                $messages += ("{0}: Filenames of more than 15 characters is not allowed" -f $filename)
            }
            return $messages
        }
        $smtpserver = $Event.MessageData.SmtpServer
        $credentials = $Event.MessageData.Credentials
        $messages = ValidateFile $Event.SourceEventArgs.Name
        if ($messages.Count -gt 0)
        {
            Send-MailMessage -From "filealerts@domain.com" -To "recipient@domain.com" -Subject "File validation" -SmtpServer $smtpserver -Body ([string]::Join("`n", $messages)) -port 587 -UseSsl -Credential $credentials
        }
    }
    
    # To disable events again.
    # Unregister-FileSystemEvent -id $eventid
    # Can also use
    # Unregister-FileSystemEvent -All
    

    【讨论】: