首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >逐行读取文件,并在每一行中打印第一个匹配项或在没有匹配时打印"no_data“

逐行读取文件,并在每一行中打印第一个匹配项或在没有匹配时打印"no_data“
EN

Stack Overflow用户
提问于 2017-01-02 17:16:08
回答 2查看 382关注 0票数 0

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

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

代码语言:javascript
复制
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可以是这样的字符串,也可以是包含各种垃圾的更大的字符串:

代码语言:javascript
复制
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 )都可以。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-01-02 21:37:43

下面是一个Perl解决方案:

代码语言:javascript
复制
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

票数 4
EN

Stack Overflow用户

发布于 2017-01-02 21:29:55

要想直接使用grep实现这一点,您必须使用某种可变长度的负后视,以确保您正在查看行中的第一次约会。显然,Perl兼容的正则表达式就能做到“回溯控制动词”兼容,但我不确定grep -P是否支持这些和b)您也希望替换不匹配的行,而grep无论如何都做不到。

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

代码语言:javascript
复制
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版本,需要三次(首先两个日期保持完整,然后删除第一个日期,然后删除两个日期),输出如下

代码语言:javascript
复制
$ 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的Bash循环:~24秒
  • Sed:~4秒
  • Perl:<0.1秒
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41430802

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档