2017-01-02 116 views
0

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

随着我有限的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的是我的罚款。

+0

听起来你只是想做'grep -o -E'[0-9] [0-9]/[0-9] [0-9]/[0-9] {4}'< tmc.txt > d.txt' –

+0

或'grep -o -E'[0-9] {1,2}/[0-9] {1,2}/[0-9] {4}'' –

+0

啊,原因你的解决方案永远不会打印“no_data”,这个头永远不会失败。在这种情况下,“头”无用,你可以将其删除。另一个常见的解决方案是在管道中的最后一个命令之后添加'| grep .'。 –

回答

4

这里的一个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

+1

这很可能会拖还与地板sed解决方案,性能明智。 –

+0

事实上,它照得很快,谢谢melpomene –

1

要直接使用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,其余命令将被跳过。他们实际上不会做任何事情,但这应该使执行速度更快。

如果线路确实包含日期,两次换人进行:第一次约会后的第一个去除一切,收到第二个的一切。这发生在两个步骤,或贪婪匹配将导致上一个日期被打印的行。对于40 MB输入文件


快速性能对比:

  • 击循环每行呼叫的grep:〜24秒
  • 桑达:〜4秒
  • 的Perl:< 0。1秒
+0

我没有尝试这种解决方案还没有,很快就会提供更新 –