FOR /f 循环命令输出中的文件名

Filename in the output of FOR /f looping through command

这是一个名为“filename1.aof”的文件中的一小部分:

SSLTE_RRCMSG|2022-01-31 18:48:30.026083|LTE
rrcConnectionReconfiguration|1510558947||DL DCCH
SSLTE_RRCMSG|2022-01-31 18:48:30.028253|LTE rrcConnectionReconfigurationComplete|1510558991||UL DCCH|10 00
SSLTE_NASEMMTIMER|2022-01-31 18:48:30.028307|0|0|0|0|0|2|0|0|0|0|0|0|0|0
Event|2022-01-31 18:48:30.123049|0|Voice|"SIP Rx"|152/3/1/0|invite sip:]:6200 sip/2.0||
Event|2022-01-31 18:48:30.123049|0|Voice|"Call Start MtoM Ter"|1/5/0/0|282||
Event|2022-01-31 18:48:30.123049|0|Voice|"SIP Connecting"|20/0/0/0|2A01:CD01:0009:271F:0000:000A:D30C:3701||
Event|2022-01-31 18:48:30.123628|0|Voice|"SIP Tx"|152/2/1/0|sip/2.0 183 session progress||
SSLTE_RRCMSG|2022-01-31 18:48:30.122785|LTE ueCapabilityEnquiry|1510559636||DL DCCH|38 00 40

我是 运行 FOR /F 正在处理 几个 这些文件,名为 *.aof :

FOR /F "tokens=2,5,7 delims=|" %G in ('findstr /I /C:"Sip Rx" /C:"Sip Tx" *.aof') DO @echo %G;%H;%I 

当前输出为:

2022-01-31 15:02:57.531681;"SIP Rx";sip/2.0 200 ok (Invite)
2022-01-31 15:02:57.532269;"SIP Tx";ack 
2022-01-31 15:03:29.037216;"SIP Tx";bye 
2022-01-31 15:03:29.337172;"SIP Rx";sip/2.0 200 ok

我需要在输出的每一行的开头重复当前文件名,如下所示:

filename1;2022-01-31 15:02:57.531681;"SIP Rx";sip/2.0 200 ok (Invite)
filename1;2022-01-31 15:02:57.532269;"SIP Tx";ack 
filename1;2022-01-31 15:03:29.037216;"SIP Tx";bye 
filename1;2022-01-31 15:03:29.337172;"SIP Rx";sip/2.0 200 ok

因为我需要识别每一行来自哪个文件。

谢谢大家!

编辑:现在使用这个命令,但它只说明文件名 before 列出提取的行...然后我需要 post-process 它在excel.

FORFILES /M *.aof /C "cmd /c echo @file & findstr /I /C:\"Sip Tx\" /C:\"Sip Rx\" @file"

数据收集任务的批处理文件

用于在所有 *.aof 文件中搜索字符串 SIP RxSIP Tx 的批处理文件,并将感兴趣的数据以所需格式写入名称为 SIP Rx_Tx.csv 的 CSV 文件是:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
(for /F "eol=? tokens=1* delims=:" %%G in ('%SystemRoot%\System32\findstr.exe /I /L /C:"SIP Rx" /C:"SIP Tx" *.aof 2^>nul') do for /F "eol=: tokens=2,5,7 delims=|" %%I in ("%%H") do echo %%G;%%I;%%J;%%K)>"SIP Rx_Tx.csv"
if exist "SIP Rx_Tx.csv" for %%I in ("SIP Rx_Tx.csv") do if %%~zI == 0 del "SIP Rx_Tx.csv"
endlocal

批处理文件的简短描述

命令FINDSTR在当前目录下的所有*.aof文件中搜索包含SIP RxSIP Tx的行并输出每行文件名在开头用冒号与找到字符串的行分隔。

外层 FOR 使用 : 作为字符串定界符将每一行分成两个子字符串(标记)。第一个子串是没有路径的文件名分配给指定的循环变量G。第二个子字符串是文件中分配给下一个循环变量 H.

的行

文件中分配给循环变量 H 的行用第二个 FOR 进一步处理,使用 | 将其拆分为几个子字符串字符串分隔符。第二个竖线分隔的子字符串分配给指定的循环变量I,第五个子字符串分配给下一个循环变量J,第七个子字符串分配给下一个循环变量K.

分配给I的文件名和找到的行中感兴趣的三个数据用命令ECHO输出。此输出被写入当前目录中名称为 SIP Rx_Tx.csv 的文件中。

创建的CSV文件如果是空文件则被删除,因为在当前目录下没有找到*.aof文件或者没有*.aof文件包含两个搜索字符串之一。

批处理文件的完整描述

这里还有一些命令行的描述,详细解释了为什么使用上面看到的命令行以及它们是如何工作的。

执行环境的定义

前两个命令行完全定义了批处理文件所需的执行环境是:

  1. 关闭命令回显模式以防止在执行过程中显示已执行的命令或将它们写入 CSV 文件。
  2. 根据 for /F 的要求启用了命令扩展。
  3. 禁用延迟扩展以处理正确的文件名和包含一个或多个感叹号的数据。

如果在处理两个 FOR 命令。如果没有这样的变量,这将导致用名称为 string 的引用变量的值替换 !string! 分别删除 !string! 。启用延迟扩展后,单个 ! 将被删除。这不是这里想要的,因此,明确禁用延迟扩展而不是依赖于批处理文件外部定义的延迟扩展的状态是一种很好的做法。

重定向运算符的正确规范 >

处理批处理文件的Windows命令处理器实例接着解析第三行包含两次redirection operator>。但只有第二个 > 用于 cmd.exe 处理批处理文件。该命令行中的第一个 > 用于另一个 cmd.exe 实例,因此必须使用脱字符 ^ 进行转义,以便 cmd.exe 处理批处理文件将其解释为文字字符。

创建输出文件

Windows 命令处理器现在首先在当前目录中创建文件 SIP Rx_Tx.csv 并覆盖具有该名称的现有文件。如果当前目录是 write-protected 或者已经有一个名为 SIP Rx_Tx.csv 的文件,根据文件系统权限或 read-only 文件属性,这可能会失败。但是,此批处理文件期望输出文件的创建成功,因为如果输出文件的创建由于某种原因失败,则批处理文件无法真正完成任务。

输出 CSV 文件保持打开状态,直到所有 *.aof 文件的处理完成,完全阻止其他进程 运行在后台打开 CSV 文件。

也可以在批处理文件中用作第三行:

for /F "eol=? tokens=1* delims=:" %%G in ('%SystemRoot%\System32\findstr.exe /I /L /C:"SIP Rx" /C:"SIP Tx" *.aof 2^>nul') do for /F "eol=: tokens=2,5,7 delims=|" %%I in ("%%H") do echo %%G;%%I;%%J;%%K>>"SIP Rx_Tx.csv"

不太好。有必要首先确保文件 SIP Rx_Tx.csv 不存在于当前目录中,例如,通过使用命令 DEL。但更有问题的是,使用此命令行,每次写入操作都会按如下方式处理 CSV 文件:

  1. CSV 文件由 cmd.exe 打开。
  2. 文件位置设置为文件末尾。
  3. 输出行附加到文件。
  4. 文件被 cmd.exe 刷新并关闭。

所以写了数百行到 te CSV 文件,CSV 文件打开、扩展和关闭数百次,效率不高。这也使得另一个进程如 anti-virus 应用程序 运行 在后台打开 CSV 文件以扫描文件中的数据以防止 cmd.exe 附加下一个数据的两次写入操作之间也成为可能到文件导致某些数据未写入 CSV 文件。

所以在整个命令行中使用 () 两个 FOR 命令和 ECHO 提高了将数据写入输出文件的效率,并防止其他进程在后台 运行 和访问 CSV 文件时意外丢失数据。

正在后台使用 FINDSTR 搜索感兴趣的数据

外部FOR循环应该运行命令FINDSTR并处理这个命令输出的行。出于这个原因,正在处理批处理文件的 cmd.exe 进程现在在后台启动,没有可见的控制台 window 一个 cmd.exe 选项 /c 和在 [ 中指定的命令行=53=] 作为附加参数附加。在C:\Windows安装Windows时执行的命令如下:

C:\Windows\System32\cmd.exe /c C:\Windows\System32\findstr.exe /I /L /C:"SIP Rx" /C:"SIP Tx" *.aof 2>nul

cmd.exe 执行的 FINDSTR 开始在后台搜索,现在首先在当前目录中搜索文件扩展名为 .aof 的长文件名或短文件名。如果没有与此通配符模式匹配的文件,则 FINDSTR 将向标准错误流 (STDERR) 输出一条错误消息,该错误消息将由cmd.exe 处理批处理文件并将错误消息输出到它自己的标准错误流,导致在控制台 window 中显示此错误消息,如果有一个控制台 window 打开 cmd.exe 处理批处理文件。该错误消息是不需要的,因此 2>nul 用于将错误消息重定向到设备 NUL 以抑制它。

FINDSTR 在当前目录

中搜索每个找到的文件扩展名为 .aof 的文件
  • case-insensitive 因为选项 /I
  • 字面意思是因为选项 /L(也因为在省略 /L 时使用 /C:
  • 对于包含字符串 SIP Rx 或字符串 SIP Tx 的行。

必须使用 /C: 才能将 space 字符解释为要搜索的文字字符,而不是 OR 表达式。使用 "SIP Rx" 作为搜索字符串将导致搜索此处不需要的字符串 SIP 或字符串 Rx。出于这个原因,两个搜索字符串是使用两次选项 /C:.

指定的

也可以使用带有参数 /I /R /C:"SIP [RT]x" *.aofFINDSTR 来使用正则表达式搜索每个 * 行中的两个字符串之一。 space 被解释为文字 space 字符的 aof 文件。

FINDSTR 输出到背景的标准输出流 (STDOUT) cmd.exe 包含两个搜索字符串之一的行文件名开头没有路径,文件中的行用冒号分隔。

所以filename1.aof有输出:

filename1.aof:Event|2022-01-31 18:48:30.123049|0|Voice|"SIP Rx"|152/3/1/0|invite sip:]:6200 sip/2.0||
filename1.aof:Event|2022-01-31 18:48:30.123628|0|Voice|"SIP Tx"|152/2/1/0|sip/2.0 183 session progress||

这是用两个 FOR 命令进一步处理的输出。

如果至少有一个 *.aof 文件并且没有找到包含两个搜索字符串之一的行,则 FINDSTR 不会输出错误消息。

通过外部 FOR

处理找到的行和输出行

处理批处理文件的cmd.exe实例通过FINDSTR[=287=捕获后台cmd.exe输出到STDOUT的所有行] 并在 findstr.exe 自行终止后处理它们,导致 cmd.exe 在后台启动并自行关闭。

for /F 总是忽略空行,这在这里没有问题,因为 FINDSTR 根本没有输出空行。

默认情况下,每个捕获的 non-empty 行会被拆分成子字符串,使用普通的 space 和水平制表符作为字符串分隔符。使用 delims=: 修改该行拆分行为以将行拆分为冒号。

检查第一个子字符串是否以行尾字符开头,默认情况下是分号。 *.aof 文件的文件名不太可能以 ; 开头,但这是可能的。因此 eol=? 用于将行尾字符更改为问号,任何文件名都不能包含该问号。

第一个子字符串被分配给指定的循环变量,默认情况下将忽略所有其他子字符串。这里不需要这种行为。第一个(系列): 之后的所有内容都应保持原样,不要将其拆分。因此 tokens=1* 用于指示 FOR 将第一个子字符串(文件名)分配给根据 ASCII table.

指定的循环变量 G 和冒号(行)之后的所有内容,而不将其拆分为下一个循环变量 H

内部FOR处理数据

分配给循环变量 H 的 *.aof 文件中将文件名分配给循环变量 G 的行必须进一步处理以获取感兴趣的数据。第二个 for /F 用于处理分配给循环变量 H.

字符串

这次使用竖线作为字符串分隔符,因为 *.aof 文件包含用 | 分隔数据的行。

行尾字符用:定义,因为分配给循环变量H的字符串绝对不是以冒号开头。如果 *.aof 文件中的一行以一个或多个冒号开头,它们将被外部 FOR 命令在拆分由 FINDSTR[ 输出的行时删除=287=] 到文件名和行。

包含SIP RxSIP Tx的每一行中感兴趣的数据是第二个|分隔子字符串中的date/time、第五个数据列中找到的字符串和第七数据列中的字符串。因此 tokens=2,5,7 用于将这三个子字符串分配给循环变量 IJK.

请注意,|| 或更多串联竖线被 for /F 解释为使用 delims=| 时的单个 |。因此 for /F 无法正确处理 CSV 文件中使用竖线作为分隔符的空数据值。在这种情况下没问题。

正在将所需格式的数据写入 CSV 文件

命令ECHO用于输出分配给循环变量G的文件名以分号为分隔符,date/time分配给循环变量I,找到的分配给循环变量 J 的字符串(带双引号)和分配给循环变量 K 的附加字符串到 cmd.exe 处理批处理的标准输出流在处理找到的行的过程中,该文件被重定向到一直打开的 CSV 文件。

使用 %%~J 会导致从找到的字符串中删除双引号。

删除为空的 CSV 文件

有可能输出的CSV文件SIP Rx_Tx.csv是在当前目录下创建的,但由于在当前工作目录下没有找到*.aof文件或者none 的 *.aof 文件包含两个搜索字符串之一。

第四个命令行检查当前目录中是否存在输出 CSV 文件及其文件大小,并删除现有但为空的文件,即文件大小为 0 字节。

初始执行环境恢复

最后,初始执行环境通过执行命令 ENDLOCAL 恢复,该命令将由 cmd.exe 在未显式写入批处理文件时隐式执行。

请阅读以了解有关命令SETLOCALENDLOCAL的详细信息。

帮助使用 Windows 命令

为了理解上面没有解释的使用的命令和它们的选项的描述,打开一个 command prompt window,在那里执行以下命令,并完整阅读每个命令显示的所有帮助页面仔细。

  • del /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • if /?
  • setlocal /?