【问题标题】:Command line utility to copy files based on their content and date命令行实用程序,用于根据文件的内容和日期复制文件
【发布时间】:2014-08-29 01:48:43
【问题描述】:

我在位置 A 有超过 10,000 个 excel 文件的列表。

  • 我需要一个免费的实用程序(因为我需要使用 Windows 任务调度程序来 每 x 分钟运行一次),它将扫描这些文件并搜索 文件中的特定字符串/内容。

  • 还应在特定日期之后创建/修改文件。

  • 如果文件符合这些条件,则必须将其复制到位置 B。

我找到了一个名为Ultrafile Search 的免费程序,它设法识别文件,但它不能使用参数等运行,所以我不能在调度程序中使用它。我还尝试了WinGrep,这似乎可以满足我的要求,但可能由于文件数量的原因,它一直处于冻结状态。命令行实用程序或批处理文件脚本会很棒。谢谢

【问题讨论】:

  • 这些是.xlsx 文件吗?
  • 批处理/cmd 无法读取 .xls.xlsx 文件。您需要在安装了 Excel 的 PC 上使用第三方工具或 VBscript,因为 VBscript 可以公开代码以读取 Excel 文件。我认为读取 10,000 个文件将是一项相当长的任务。

标签: excel batch-file powershell scripting command-line-arguments


【解决方案1】:

只是一个基本的骨架。根据需要进行调整。

这是一个混合批处理/jscript 文件(另存为.cmd)。它使用robocopy 首先选择指定日期范围内的文件。生成的列表被传递给 javascript 部分以过滤 excel 文件的内容。 javascript 充当过滤器,仅回显带有指定字符串的文件名。此列表由for 命令检索/处理(在示例中,它只是将文件名回显到控制台)

当然,要使用它,必须安装 excel。

@if (@This==@IsBatch) @then
@echo off
rem **** batch zone *********************************************************
    setlocal enableextensions disabledelayedexpansion

    set "sourceFolder=%cd%"
    set "dateStart=20140709"
    set "searchString=testing"

    set "dateFilter=robocopy "%sourceFolder%" "%temp%" *.xls /l /njh /njs /ndl /nc /ns /maxage:%dateStart% "
    set "contentFilter=cscript //nologo //e:Javascript "%~f0" /search:"%searchString%" "

    for /f "delims=" %%a in (' %dateFilter% ^| %contentFilter% ') do (
        echo Selected file : [%%a]
    )

    endlocal
    exit /b

@end
// **** Javascript zone *****************************************************

    // retrieve the string to search for
    var searchFor = WScript.Arguments.Named('search');

    // instantiate needed components
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    var excel = new ActiveXObject('Excel.Application');

    // iterate over the stdin reading file names to process
    while ( ! WScript.StdIn.AtEndOfStream ){
        var fileName = WScript.StdIn.ReadLine();
        fileName = fileName.replace(/^\s*/g,'');
        fileName = fso.GetAbsolutePathName(fileName);
        if (fso.FileExists( fileName )){
            if (excelDataSearch( excel, fileName, searchFor )) {
                WScript.StdOut.WriteLine( fileName );
            };
        };
    };

    // if not needed, close the instantiated excel application
    if ( excel.WorkBooks.Count === 0 ) excel.Quit();

    // leave
    WScript.Quit(0);


function excelDataSearch( excel, workbook, searchString ){
    var returnValue = false ;

    try {
        // search for workbook application
        //var wb = GetObject( workbook );
        var wb = excel.Workbooks.Open( workbook, false, true );

        // iterate the worksheets of the workbook searching the string in its data
        var eWS = new Enumerator(wb.WorkSheets);
        for (; !eWS.atEnd() ; eWS.moveNext()){
            var ws = eWS.item();
            if ( ws.UsedRange.Cells.Find(searchString) ){
                returnValue = true ;
                break;
            };
        };

        // close workbook
        wb.Close( false );

    } catch(e){
            // some kind of problem with objects
            // WScript.Echo( e.description );
    };

    return returnValue;
};

【讨论】:

    【解决方案2】:

    PowerShell解决方案

    • 递归查找c:\temp下的所有excel文件
    • 检查它们是否自 2010 年 1 月 1 日起使用过
    • 检查工作表 1 中的单元格 A1 是否等于变量 str

    如果是,则将文件复制到新目录 (c:\test)

    $newpath = "c:\test"
    $str = "Test"
    $xl = New-Object -comobject Excel.Application
    $xl.visible = $false
    
    $excelSheets = Get-ChildItem c:\temp -recurse -include *.xls*  | where-object {$_.lastwritetime -gt “1/1/2010}
    
    foreach($excelSheet in $excelSheets)
    {
    $workbook = $xl.Workbooks.Open($excelSheet)
    $ws = $workbook.workSheets.Item(1)
    $strxl = $ws.Cells.Item(1,1).Value2
    $workbook.close()
    if ($strxl -eq $str) {Copy-Item $excelsheet $newpath}
    }#end foreach
    $xl.quit()
    $xl = $null
    [gc]::collect()
    [gc]::WaitForPendingFinalizers()
    

    【讨论】:

    • 非常感谢@brettdj - 您的解决方案也非常好用!
    最近更新 更多