【问题标题】:Read file line by line and print the first match in each line or "no_data" when nothing matches逐行读取文件并打印每行中的第一个匹配项或当没有匹配项时打印“no_data”
【发布时间】:2017-05-16 19:04:58
【问题描述】:

我想逐行读取文本文件以搜索模式;当找到一行中的第一个匹配项时,将其打印到文件中并移动以搜索下一行中的模式。

由于我在 shell 方面的技能有限,我尝试了以下方法;不幸的是,当没有第一个模式时,它永远不会将no_data 打印到文件d.txt

while read u ; do
    echo "$u" | grep -o '[0-9]\{2\}/[0-9]\{2\}/[0-9]\{4\}  [0-9]\{2\}:[0-9]\{2\}' |head -1 || echo "no_data" 
done < tmc.txt > d.txt

注意:我尝试匹配的模式是mm/dd/yyyy hh:mm 格式的日期和时间戳。

例如,$u 可以是这样的字符串,甚至更大的字符串会包含各种垃圾:

disk0/bcdackup_20160908_115716/d/.ER/ERORR_log_msnf_20160906_113039:10641:  Test Status:         Failed ;Test PL (some test) was started in execution mode.  09/06/2016  14:43:28.4954  Machine:msnf  (Rl888751, , ?.?, 1637) USER EVENT: TM-1102 DEFAULT  -- SYSTEM ERROR: TX-0003 INIT  Function Protocol Violation. Verification by TXXAxREQxConfig_destroy_config failed: 'engine_ptr != NULL' not TRUE  -- SYSTEM EVENT: ER-0FFF DEFAULT (linked to IH-154B) DEACTIVATE: IH-154b DEACTIVATE: IH-154b  -- SYSTEM EVENT: ER-0FFF DEFAULT (linked to IH-154C) DEACTIVATE: IH-154c DEACTIVATE: IH-154c  -- SYSTEM ERROR: WP-2631 CHANGEPARAMS  Error during processing of Finite State Machine Error starting perform_smooth_landing : event perform_smooth_landing not allowed in state {original_mc, actuator_system_enabled, service_off, not_homed} of state-machine WPLS.V1.2  -- SYSTEM ERROR: WP-2630 CHANGEPARAMS  Error during processing of F   

我可以使用任何 shell 实用程序,例如 grep、awk、sed、perl。

【问题讨论】:

  • 听起来你只是想做grep -o -E '[0-9][0-9]/[0-9][0-9]/[0-9]{4}' &lt; tmc.txt &gt; d.txt
  • grep -o -E '[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}'
  • 啊,您的解决方案从不打印“no_data”的原因是 head 永远不会失败。在这种情况下,head 没有用,您可以将其删除。另一种常见的解决方案是在管道中的最后一个命令之后添加|grep .
  • 嗨,威廉,如果我使用您对 grep -o -E 的建议,那么对于包含多个感兴趣模式的行,它将打印多个模式
  • 请记住,我只对该文件中每一行的第一个匹配项感兴趣

标签: linux perl shell grep


【解决方案1】:

这是一个 Perl 解决方案:

perl -nle 'print m{(\d{2}/\d{2}/\d{4} \d{2}:\d{2})} ? $1 : "no_data"' < tmc.txt > d.txt

-n 循环输入中的行。

-l 自动从输入中剔除换行符并将它们添加到输出中。

对于每一行,我们使用捕获组进行简单的正则表达式匹配。如果成功,我们打印匹配的字符串,否则为no_data

【讨论】:

  • 这很可能还会用 sed 解决方案拖地,从性能方面来说。
【解决方案2】:

要直接使用 grep 执行此操作,您必须使用某种可变长度的负数后视来确保您正在查看该行中的第一个日期。显然,Perl 兼容的正则表达式 would be able to do that"backtracking control verbs",但是 a) 我不确定 grep -P 是否支持这些,并且 b) 你还想替换不匹配的行,而 grep 无论如何也做不到。

作为在每一行调用 grep 的替代方法,您可以使用 sed:

sed -r '
    /([0-9]{2}\/){2}[0-9]{4} +[0-9]{2}:[0-9]{2}/! { # On non-matching lines...
        s/.*/no_data/                               # Replace line with "no_data"
        b                                           # Skip to next line
    }
    s/(([0-9]{2}\/){2}[0-9]{4} +[^ ]*).*/\1/ # Remove everything after first date
    s/.*(([0-9]{2}\/){2}[0-9]{4})/\1/        # Remove everything before first date
' infile

对于使用示例行三次的infile 版本(首先两个日期均完好,然后删除第一个日期,然后删除两个日期),输出为

$ sed -r '/([0-9]{2}\/){2}[0-9]{4} +[0-9]{2}:[0-9]{2}/!{s/.*/no_data/;b};s/(([0-9]{2}\/){2}[0-9]{4} +[^ ]*).*/\1/;s/.*(([0-9]{2}\/){2}[0-9]{4})/\1/' infile
09/06/2016  14:43:28.4954
08/06/2016  18:53:28.4757
no_data

正如预期的那样。

sed 命令首先检查该行是否包含日期;如果不是,则整行将替换为no_data,并跳过其余命令。他们实际上不会做任何事情,但这应该会加快执行速度。

如果行确实包含日期,则执行两次替换:第一个删除第一个日期之后的所有内容,第二个删除它之前的所有内容。这必须分两步发生,否则贪婪匹配会导致打印行上的last日期。


40 MB 输入文件的快速性能比较:

  • 在每行调用 grep 的 Bash 循环:~24 秒
  • Sed:~4 秒
  • Perl:

【讨论】:

  • 我还没有尝试这个解决方案,很快就会提供更新
猜你喜欢
  • 2015-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多