【问题标题】:Extract Part of a text file in BAT在 BAT 中提取文本文件的一部分
【发布时间】:2016-06-29 09:00:18
【问题描述】:

我每天都在捕获一个 m3U 文件,但希望用我需要的几个频道将其中的一部分解析为另一个文件。

例如,我已将我的 m3U 重命名为 Test.txt 文件,该文件具有以下虚构结构:

#EXTINF:0,ABC  
#live link 1
#EXTINF:0,XYZ   
#live link 2
#EXTINF:0,UVW  
#live link 3

我只想捕获说从“#EXTINF:0,XYZ”开始的行,然后说它下面的行以如下所示的 Output.txt 结尾:

#EXTINF:0,XYZ   
#live link 2

我知道需要使用 For 循环,但我在这方面有点菜鸟。

【问题讨论】:

  • 是的,它可以有空格。
  • XYZ 部分是否可能包含空格?在您的示例数据中可以有尾随空格吗?

标签: batch-file for-loop


【解决方案1】:

将此代码放入文件filter.cmd。

@echo off
set INPUT=%1&set MATCH=%2& set MATCHED=0
for /f "delims=" %%a in (%INPUT%) do call :line "%%~a"
goto :eof
:line
set EXT=&TITLE=&
for /f "tokens=1 delims=:" %%a in ("%~1") do set EXT=%%~a
for /f "tokens=1,2,* delims=:," %%a in ("%~1") do set TITLE=%%~c
if "%EXT%" == "#EXTM3U" echo %~1
if "%EXT%" == "#EXTINF" (
  set MATCHED=0
  echo %TITLE%| findstr /l %MATCH% >nul  && set MATCHED=1
)
if %MATCHED%==1 echo %~1

使用示例:

filter.cmd input_file.m3u XYZ > output_file.m3u

这里有一些解释:
每个输入行都使用 for /f 与标记和分隔符进行拆分。 如果该行以#EXTINF 开头并且其余包含要匹配的字符串(第二个参数),则设置 MATCHED。 如果设置了 MATCHED,则输出这些行直到下一个 #EXTINF。

【讨论】:

  • 1.通过for /F 将每一行解析两次并不是很有效,您可以轻松地将它们组合起来。 2. 应将%1 替换为%~1%2 相同。 3. 语法set "VAR=Value" 对特殊字符更安全(例如,如果字符串包含& 怎么办?)。 4. 您应该在findstr 命令行中将%MATCH% 放在"" 之间。 5. 我建议提供开关/Xfindstr 以匹配整个标题和/或/C 开关以定义文字搜索字符串(请注意,如果没有/C,搜索中的空格字符串构成单词分隔符)。
  • 1.我没有试图展示一个防弹和最小的解决方案,如果我这样做了,我无论如何都会用另一种语言来做。重点是说明 for 循环的使用。 2-4。无需将 %1 替换为 %~1,引用 set =,MATCH,INPUT,因为它们来自已正确引用的命令行。如果有人使用包含 & 的不带引号的参数运行脚本,您将无能为力。
  • 正确引用在批处理中非常很重要:使用%~1 删除引号,在代码中声明"%~1" 确保正确引用; %1 按原样扩展为参数,%~1 删除周围的引号,因此 "%~1" 是引用字符串的最安全方式("%1" 如果已经以引用方式提供,甚至可能导致过度引用的字符串);引号避免了字符串中出现的空格(标记分隔符)和特殊字符的问题;尽管如此,您的回答确实说明了for /F 的工作原理......
  • 这些都是众所周知的事实,我必须再次强调,在以下情况下不需要替换 %1: 1. %1 以引号开头和结尾,在这种情况下,它等同于 " %~1" 2. %1 不包含特殊字符,在这种情况下,按原样使用它是安全的。当然,仅将 %1 替换为 %~1 是错误的,以及将 %1 替换为 "%1"。
  • 使用"%~1"是最安全的方式,这就是我想说的;虽然没有最终的包罗万象的方式(例如考虑像"&"& 这样的参数),但是由于没有引号参数的限制,"%~1" 是安全的......
【解决方案2】:

我会这样做,假设 .m3u 文件在 #EXTINF 前面的行中不包含尾随空格,就像您的示例数据一样:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "FILE=%~1"
set "HEADER=#EXTM3U"
set "PREFIX=#EXTINF"
set "MATCH=%~2"

set "FLAG="
for /F usebackq^ delims^=^ eol^= %%L in ("%FILE%") do (
    if defined FLAG (
        echo(%%L
        set "FLAG="
    )
    for /F "delims=:" %%P in ("%%L") do (
        if "%%P"=="%HEADER%" (
            echo(%%L
        ) else if "%%P"=="%PREFIX%" (
            set "LINE=%%L"
            setlocal EnableDelayedExpansion
            if /I "!LINE:*,=!"=="!MATCH!" (
                echo(!LINE!
                endlocal
                set "FLAG=#"
            ) else endlocal
        )
    )
)

endlocal
exit /B

这样调用脚本,假设保存为extract-entry.bat:

extract-entry.bat "input_file.m3u" "XYZ" > "output_file.m3u"

脚本逐行遍历给定的.m3u 文件。如果设置了变量FLAG,则返回未编辑的当前行并重置变量FLAG,而开头并非如此。

然后它寻找#EXTINF。如果找到(例如,#EXTINF:0,XYZ),则将逗号后面的字符串(XYZ)与给定的搜索字符串进行比较。如果匹配,则输出当前行并设置FLAG 变量,以便也获得以下行。

标题行#EXTM3U 总是输出。

切换delayed expansion 使该脚本对所有对命令解释器具有特殊意义的字符具有鲁棒性,而不会丢失它们。

【讨论】:

  • 谢谢 - 这似乎适用于一行。但是我想将此命令包含在另一个 bat 文件中,并将多个通道提取到各种文件中...... bat 文件在我提取的第一个通道之后刚刚完成。
  • 多于 1 个频道 是什么意思?您的意思是提取多个#EXTINF: 部分,还是在一个#EXTINF: 部分之后提取多个行?
  • 很抱歉没有很好地解释自己......我设法通过在 extract-entry.bat 前面使用 CALL 来克服这个问题。我都工作得很好。感谢您的帮助!
  • 啊,我明白了,你从另一个脚本中调用它;是的,你需要使用call,否则,执行将不会返回到调用者脚本...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-14
  • 2018-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 1970-01-01
相关资源
最近更新 更多