2017-01-02 128 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

我沒有嘗試這種解決方案還沒有,很快就會提供更新 –