【问题标题】:How to edit 1st instance of text in multiple htm files using batch command?如何使用批处理命令编辑多个 htm 文件中的第一个文本实例?
【发布时间】:2015-02-10 22:26:06
【问题描述】:

我需要使用批处理命令删除多个 .htm 文件中的第一个

标记实例,所有这些文件都位于一个目录中。任何建议。

编辑 - 我刚刚意识到 .htm 文件中可能有多个 DIV,因此我只需要删除每个 DIV 中

标记的第一个实例(如果任何)。澄清一下,我只希望删除标签,但希望保留标签之间的内容/文本。感谢迄今为止的答案/cmets!!!

至于为什么,长话短说,但只知道我在一家与供应商签订合同的代理机构工作,该供应商没有测试我们用 IE11 支付的版本。因此,只有第一个段落标记(当有多个段落时)会使所有文本显示比预期低 15 个像素。我无法更改或修改供应商的代码,但是,我可以在导出电子学习课程后对其进行修改。这就是我需要这个批处理文件的目的。如果我只删除每页上段落标记的第一个实例,则整个文本将按预期显示。

【问题讨论】:

  • 您是否也需要删除标签之间的任何内容?

标签: batch-file text edit internet-explorer-11


【解决方案1】:

最安全的解决方案(尽管可能是最慢和最复杂的)是将您的 HTML 文件解析为 HTML 并从 DOM 中删除第一段。这将给您带来的好处是不受任何可靠的 HTML 源格式的限制。正确跳过评论,正确处理换行符,生活充满阳光和雏菊。可以使用InternetExplorer.Application COM object 解析 HTML DOM。这是一个批处理/JScript 混合示例:

@if (@CodeSection == @Batch) @then

@echo off
setlocal

for %%I in (*.html) do (
    cscript /nologo /e:JScript "%~f0" "%%~fI"
)

rem // end main runtime
goto :EOF

@end
// end batch / begin JScript chimera

WSH.Echo(WSH.Arguments(0));

var fso = WSH.CreateObject('scripting.filesystemobject'),
    IE = WSH.CreateObject('InternetExplorer.Application'),
    htmlfile = fso.GetAbsolutePathName(WSH.Arguments(0));

IE.Visible = 0;
IE.Navigate('file:///' + htmlfile.replace(/\\/g, '/'));
while (IE.Busy || IE.ReadyState != 4) WSH.Sleep(25);

var p = IE.document.getElementsByTagName('p');

if (p && p[0]) {

    /* If you want to remove the surrounding <p></p> only
    while keeping the paragraph's inner content, uncomment
    the following line: */

    // while (p[0].hasChildNodes()) p[0].parentNode.insertBefore(p[0].firstChild, p[0]);

    p[0].parentNode.removeChild(p[0]);
    htmlfile = fso.CreateTextFile(htmlfile, 1);
    htmlfile.Write('<!DOCTYPE html>\n'
        + '<html>\n'
        + IE.document.documentElement.innerHTML
        + '\n</html>');
    htmlfile.Close();
}

IE.Quit();
try { while (IE && IE.Busy) WSH.Sleep(25); }
catch(e) {}

而且由于您使用的是 DOM,因此可以更轻松地进行其他调整。要删除每个 &lt;div&gt; 元素中的第一个 &lt;p&gt; 元素(只是作为一个狂野的例子,并不是任何人都会想要这个),像你一样导航 DOM在基于浏览器的 JavaScript 中。

@if (@CodeSection == @Batch) @then

@echo off
setlocal

for %%I in ("*.htm") do (
    echo Batch section: "%%~fI"
    cscript /nologo /e:JScript "%~f0" "%%~fI"
)

rem // end main runtime
goto :EOF

@end
// end batch / begin JScript chimera

WSH.Echo('JScript section: "' + WSH.Arguments(0) + '"');

var fso = WSH.CreateObject('scripting.filesystemobject'),
    IE = WSH.CreateObject('InternetExplorer.Application'),
    htmlfile = fso.GetAbsolutePathName(WSH.Arguments(0)),
    changed;

IE.Visible = 0;
IE.Navigate('file:///' + htmlfile.replace(/\\/g, '/'));
while (IE.Busy || IE.ReadyState != 4) WSH.Sleep(25);

for (var d = IE.document.getElementsByTagName('div'), i = 0; i < d.length; i++) {

    var p = d[i].getElementsByTagName('p');
    if (p && p[0]) {

        // move contents of p node up to parent
        while (p[0].hasChildNodes()) p[0].parentNode.insertBefore(p[0].firstChild, p[0]);

        // delete now empty p node
        p[0].parentNode.removeChild(p[0]);
        changed = true;
    }
}

if (changed) {
    htmlfile = fso.CreateTextFile(htmlfile, 1);
    htmlfile.Write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n'
        + '<HTML xmlns:t= "urn:schemas-microsoft-com:time" xmlns:control>\n'
        + IE.document.documentElement.innerHTML
        + '\n</HTML>');
    htmlfile.Close();
}

IE.Quit();
try { while (IE && IE.Busy) WSH.Sleep(25); }
catch(e) {}

【讨论】:

  • 看起来和听起来都是一个可靠的解决方案 - 让我投票!您是否愿意解释一下混合方面以及是否将其保存为.BAT 文件以及如何运行它?
  • 是的,您将它保存为.bat 文件并像运行任何其他.bat 脚本一样运行它。 Batch 将第一行评估为 false 并继续到下一行。当批处理线程到达cscript 行时,脚本被重新评估为JScript。 JScript 将第一行评估为假并跳到@end。当 JScript 到达脚本末尾时,它将控制权返回给批处理。然后批处理执行下一个循环迭代。 See this page 了解更多混合批处理脚本示例。
  • 您在哪个版本的 Windows 上运行它?我在家里和工作中运行 Win7 x64;上班装IE9,家里装IE11。它在两个地方都没有错误地运行。您的 html 文件的路径是否包含任何奇怪的字符?我认为这无关紧要,但无论如何我想排除它作为一个原因。
  • 我确实更改了您的代码以搜索 .htm 文件而不是 .html,因为它们都是 .htm 文件。我还取消了注释您的代码以将文本保留在标签之间。但是,我也意识到有些页面有多个DIV,我只需要删除每个.htm页面中每个DIV中的第一个段落标签。
  • 所以,我现在意识到发生了什么......当我复制和粘贴时,它打破了 Navigate 行并添加了! html文件之间。并更换。所以现在当我运行它时,它在 IE11 中打开了所有 68 个页面并冻结了我的笔记本电脑。
【解决方案2】:

您可能期望的解决方案,一个纯批处理解决方案,将涉及一堆for 循环。此示例将删除从第一个 &lt;p&gt; 到第一个 &lt;/p&gt; 的整行。

我确信 npocmaka、MC ND、Aacini、jeb 或 dbenham 可以用一半的代码和十倍的效率完成此任务。 *耸耸肩*

这是一种中间解决方案,与 PowerShell 正则表达式替换相比,&lt;p&gt; 标记中的换行提供了更多的容忍度,但不如 InternetExplorer.Application COM 对象 JScript 混合那么安全。

@echo off
setlocal

for %%I in (*.html) do (

    set p_on_line=

    rem // get line number of first <p> tag
    for /f "tokens=1 delims=:" %%n in (
        'findstr /i /n "<p[^ar]" "%%~fI"'
    ) do if not defined p_on_line set "p_on_line=%%n"

    if defined p_on_line (

        rem // process file line-by-line
        setlocal enabledelayedexpansion
        for /f "delims=" %%L in ('findstr /n "^" "%%~fI"') do (
            call :split num line "%%L"

            rem // If <p> has not yet been reached, copy line to new file
            if !num! lss !p_on_line! (
                >>"%%~dpnI.new" echo(!line!
            ) else (
                rem // If </p> has been reached, resume writing.
                if not "!line!"=="!line:</p>=!" set p_on_line=2147483647
            )
        )
        endlocal
        if exist "%%~dpnI.new" move /y "%%~dpnI.new" "%%~fI" >NUL
    )
)

goto :EOF

:split <num_var> <line_var> <string>
setlocal disabledelayedexpansion
set "line=%~3"
for /f "tokens=1 delims=:" %%I in ("%~3") do set "num=%%I"
set "line=%line:*:=%"
endlocal & set "%~1=%num%" & set "%~2=%line%"
goto :EOF

【讨论】:

    【解决方案3】:
    @ECHO Off
    SETLOCAL
    SET "sourcedir=U:\sourcedir"
    SET "destdir=U:\destdir"
    PUSHD "%sourcedir%"
    FOR /f "delims=" %%f IN ('dir /b /a-d "q28443084*" ') DO ((
     SET "zap=<P>"
     FOR /f "usebackqdelims=" %%a IN ("%%f") DO (
      IF DEFINED zap (
       SET "line=%%a"
       CALL :process
       IF DEFINED keep (ECHO(%%a) ELSE (iF DEFINED line CALL ECHO(%%line%%)
      ) ELSE (ECHO(%%a)
     )
     )>"%destdir%\%%f"
    )
    popd
    
    GOTO :EOF
    
    :process
    SET "keep="
    CALL SET "line2=%%line:%zap%=%%"
    IF "%line%" equ "%line2%" SET "keep=y"&GOTO :EOF
    SET "line=%line2%"
    IF "%zap%"=="</P>" SET "zap="&GOTO :EOF 
    SET "zap=</P>"
    IF NOT DEFINED line GOTO :EOF 
    SET "line=%line2:</P>=%"
    IF "%line%" neq "%line2%" SET "zap="
    GOTO :eof
    

    这可能有效 - 它会抑制空行。

    我选择处理与 u:\sourcedir 目录中的掩码 q28443084* 匹配的文件以匹配 u:\destdir 中的文件名 - 您需要更改这些设置以适应。

    该过程围绕zap 的设置进行,可以设置为&lt;P&gt;&lt;/P&gt;nothing。检查传入的行,如果它不包含zap,则保持原样,或者以修改的形式输出并将zap调整为下一个值。如果zap什么都没有,那么只需将输入复制到输出。

    【讨论】:

    • 啊,我知道我忘记了一个人。
    【解决方案4】:

    最短的解决方案是使用 PowerShell one-liner。

    powershell -command "gci '*.html' | %{ ([regex]'<p\W.*?</p>').replace([IO.File]::ReadAllText($_),'',1) | sc $_ }"
    

    请注意,这只有在第一段中没有换行符时才有效。如果&lt;p&gt;&lt;/p&gt; 之间有换行符,它将继续搜索,直到找到一个段落没有换行符。与这种骇人听闻的解决方法相比,尝试修复供应商损坏的 CSS 可能会更好。

    不管怎样,上面的命令大致是这样翻译的:

    • 在当前目录下,获取匹配*.html的子项
    • 对于每个匹配的 html 文件(%foreach-object 的别名):

      • 创建一个匹配从&lt;p到闪亮&lt;/p&gt;的正则表达式对象
      • 使用以下参数调用该正则表达式对象的 replace 方法:

        • 使用 HTML 文件内容作为大海捞针,
        • 空针更换,
        • 并执行 1 次。
      • 将HTML文件的内容设置为结果。

    我使用[IO.File]::ReadAllText($_) 而不是gc $_ 来保留换行符。使用get-content[regex].replace mashes everything together 成一行。我使用了[regex] 对象而不是更简单的-replace 开关,因为-replace 是全局的。

    【讨论】:

      【解决方案5】:

      这是与HTML DOM answer 类似的解决方案。如果您的 HTML 有效,您可以尝试将其解析为 XML。这里的优点是,InternetExplorer.Application COM 对象为每个页面加载加载整个完全膨胀的 Internet Explorer 实例,而不是只加载一个 dll (msxml3.dll)。这应该有望更有效地处理多个文件。不利的一面是 XML 解析器对标签结构的有效性很挑剔。例如,如果您有一个未关闭列表项的无序列表:

      <ul>
          <li>Item 1
          <li>Item 2
      </ul>
      

      ... Web 浏览器可以理解这一点,但 XML 解析器可能会出错。无论如何,值得一试。我刚刚在一个包含 500 个相同 HTML 文件的目录上进行了测试,不到一分钟就完成了。

      @if (@CodeSection == @Batch) @then
      
      @echo off
      setlocal
      
      for %%I in ("*.htm") do (
          cscript /nologo /e:JScript "%~f0" "%%~fI"
      )
      
      rem // end main runtime
      goto :EOF
      
      @end
      // end batch / begin JScript chimera
      
      WSH.StdOut.Write('Checking ' + WSH.Arguments(0) + '... ');
      
      var fso = WSH.CreateObject('scripting.filesystemobject'),
          DOM = WSH.CreateObject('Microsoft.XMLDOM'),
          htmlfile = fso.OpenTextFile(WSH.Arguments(0), 1),
          html = htmlfile.ReadAll().split(/<\/head\b.*?>/i),  
          head = html[0] + '</head>',
          body = html[1].replace(/<\/html\b.*?>/i,''),
          changed;
      
      htmlfile.Close();
      
      // attempt to massage body string into valid XHTML
      var self_closing_tags = ['area','base','br','col',
          'command','comment','embed','hr','img','input',
          'keygen','link','meta','param','source','track','wbr'];
      
      body = body.replace(/<\/?\w+/g, function(m) { return m.toLowerCase(); }).replace(
          RegExp([    // should match <br>
              '<(',
                  '(' + self_closing_tags.join('|') + ')',
                  '([^>]+[^\/])?',    // for tags with properties, tag is unclosed
              ')>'
          ].join(''), 'ig'), "<$1 />"
      );  
      
      DOM.loadXML(body);
      DOM.async = false;
      
      if (DOM.parseError.errorCode) {
         WSH.Echo(DOM.parseError.reason);
         WSH.Quit(0);
      }
      
      for (var d = DOM.documentElement.getElementsByTagName('div'), i = 0; i < d.length; i++) {
      
          var p = d[i].getElementsByTagName('p');
          if (p && p[0]) {
      
              // move contents of p node up to parent
              while (p[0].hasChildNodes()) p[0].parentNode.insertBefore(p[0].firstChild, p[0]);
      
              // delete now empty p node
              p[0].parentNode.removeChild(p[0]);
              changed = true;
          }
      }
      
      html = head + DOM.documentElement.xml + '</html>';
      
      if (changed) {
          htmlfile = fso.CreateTextFile(WSH.Arguments(0), 1);
          htmlfile.Write(html);
          htmlfile.Close();
          WSH.Echo('Fixed!');
      }
      else WSH.Echo('Nothing to change.');
      

      【讨论】:

      • @Kelly 试试这个编辑。 “名称以无效字符开头”是我在测试中从 &lt;html&gt; 标记中删除无用 xmlns 垃圾之前遇到的错误。我决定无论如何都没有理由尝试解析&lt;/head&gt; 之前的任何内容,因此此修订版仅将&lt;body&gt;&lt;/body&gt; 的内容传递给XML 解析器。看看你是否有更好的运气。如果这不起作用,我可能需要您粘贴一个示例 .htm 文件,以便我可以尝试找出阻塞 XML 解析器的具体原因,并对其进行处理。
      • @Kelly Dude,这段代码一团糟。我很难将这个圆形钉子安装到 XML 解析器的方孔中。但是上面的InternetExplorer.Application 脚本运行良好。我在一个文件夹中复制了示例 htm 文件 500 次并运行了脚本,它在大约一分钟内完成了全部 500 次。它比 XML 解析器慢,但并不痛苦,而且我没有收到任何 ActiveX 警告,也没有显示任何 IE 窗口。你有没有机会在另一台电脑上重新拍摄昨天下午的剧本?
      • LOL - 是的,我知道代码是一团糟。必须爱供应商。无论如何,我重试了上面的 IE 应用程序脚本,我不断打开 60 个 IE 窗口,并在第 26,1 行出错。它说(空):未指定的错误
      • 尝试在目录中仅使用一个 htm 文件运行它。 IE 窗口是否仍然打开?它是在请求许可还是抱怨某事被阻止?控制台中的错误是否仍然显示“未指定错误”?
      • 那么 IE 是否抱怨某些内容被阻止了?我敢打赌,如果您在家用计算机上也这样做,它会按预期工作。我正在努力了解您的计算机操作环境与我的不同之处。正如我昨天所说,该脚本适用于我的域连接 Windows 7 Enterprise w/IE9 和我的家庭 Windows 7 Home Edition w/IE11。哦,好吧,让我赶上一些电子邮件,也许我会尝试重写 XML 解析器,只是为了自己加载和修复 div 标签。 (在/&lt;\/?div\b.*?&gt;/g 或其他地方拆分html。)
      【解决方案6】:

      为了后代,我找到了另一种解决方案。 O.P. 在浏览器安全和组策略限制方面存在问题,导致 InternetExplorer.Application COM 对象无法按预期运行,并且他修复的 HTML 无法合理地转换为 Microsoft.XMLDOM 解析器的有效 XML。但我很乐观,htmlfile COM 对象不会遭受这些相同的缺陷。

      当我通过电子邮件发送给 O.P. 时:

      在 Google 搜索中,我发现偶尔引用了一个名为“htmlfile”的神秘 COM 对象。它似乎是一种在不使用 IE 引擎的情况下构建 HTML DOM 并与之交互的方法。我在 MSDN 上找不到任何关于它的文档,但我设法从反复试验中收集了足够多的方法和属性以使脚本正常工作。

      从那以后我发现htmlfile COM 对象比我们看到的更多——例如htmlfileObj.parentWindow.clipboardData (MSDN reference)。

      无论如何,我对这个解决方案最乐观,但 O.P. 已停止回复我的电子邮件。不过,也许它对其他人有用。

      @if (@CodeSection == @Batch) @then
      
      @echo off
      setlocal
      
      for %%I in ("*.htm") do cscript /nologo /e:JScript "%~f0" "%%~fI"
      
      rem // end main runtime
      goto :EOF
      
      @end
      // end batch / begin JScript chimera
      
      WSH.StdOut.Write(WSH.Arguments(0) + ': ');
      
      var fso = WSH.CreateObject('scripting.filesystemobject'),
          DOM = WSH.CreateObject('htmlfile'),
          htmlfile = fso.OpenTextFile(WSH.Arguments(0), 1),
          html = htmlfile.ReadAll(),
          head = html.split(/<body\b.*?>/i)[0],
          bodyTag = html.match(/<body\b.*?>/i)[0],
          changed;
      
      DOM.write(html);
      htmlfile.Close();
      
      if (DOM.getElementsByName('p_tag_fixed').length) {
          WSH.Echo('fix already applied.');
          WSH.Quit(0);
      }
      
      for (var d = DOM.body.getElementsByTagName('div'), i = 0; i < d.length; i++) {
      
          var p = d[i].getElementsByTagName('p');
          if (p && p[0]) {
      
              // move contents of p node up to parent
              while (p[0].hasChildNodes()) p[0].parentNode.insertBefore(p[0].firstChild, p[0]);
      
              // delete now empty p node
              p[0].parentNode.removeChild(p[0]);
      
              changed = true;
          }
      }
      
      if (changed) {
          htmlfile = fso.CreateTextFile(WSH.Arguments(0), 1);
          htmlfile.Write(
              head
              + '<meta name="p_tag_fixed" />'
              + bodyTag
              + DOM.body.innerHTML
              + '</body></html>'
          );
          htmlfile.Close();
          WSH.Echo('Fixed!')
      }
      else WSH.Echo('unchanged.');
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-29
        • 1970-01-01
        • 1970-01-01
        • 2016-04-27
        • 2017-11-30
        • 2015-05-03
        • 1970-01-01
        • 2013-02-10
        相关资源
        最近更新 更多