逐行读取文件并打印每行中的第一个匹配项或" no_data"什么都不匹配

时间:2017-01-02 17:16:08

标签: linux perl shell grep

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

由于我在外壳方面的技能有限,我尝试了以下方法;遗憾的是,当没有第一个模式时,它永远不会将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都可以。

2 个答案:

答案 0 :(得分: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 :(得分: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替换,并跳过其余命令。他们实际上什么也做不了,但这应该会让执行更快。

如果包含日期,则会执行两次替换:第一次删除第一个日期之后的所有内容,第二个删除之前的所有内容。此分两步执行,或者贪婪匹配会导致正在打印的行上的 last 日期。

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

  • 在每一行上调用grep的Bash循环:~24秒
  • Sed:~4秒
  • Perl:&lt; 0.1秒