Grep 位于不同行的多个模式并在同一行中打印

Grep 位于不同行的多个模式并在同一行中打印

我试图找到一种方法来 grep 位于不同行的文件中的信息,其中一个模式是包含日期和时间的模式。

下面是输入

unwantedtext unwantedtext unwantedtext unwantedtext 8/1/2022 6:15 (1st required pattern using date and time format and the date and time can be changed)

unwantedtext unwantedtext unwantedtext unwantedtext 
unwantedtext unwantedtext INC-220721-00007628 (2nd required pattern)

同样在第二个文件中,某些第二模式并不总是位于第三行,但肯定位于与日期和时间信息不同的行中。

第二个模式始终以 INC 开头,然后是年月和日期信息

INC

始终将此 INC 放在前 3 位数字中

INC-YYMMDD

INC 之后是日期和时间

INC-YYMMDD-00000000

YYMMDD 之后是随机 8 位数字。

-

连字符始终作为第二个模式的分隔符

我期待以下输出

INC-220721-00007628,8/1/2022 6:15 

对于测试,我使用 Windows CYGWYN,对于大量文件,我使用 CENTOS 7

预先感谢您的所有建议

答案1

假设值为总是成对地,您可以用来grep查找它们并将paste它们组合起来:

$ grep -oP '\b\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{2}|INC-\d{6}-\d{8}\b' file | 
  paste -d "," - - 
8/1/2022 6:15,INC-220721-00007628

答案2

你无法grep独自完成这件事。您也许可以sed使用 PITA 来完成此操作,并且需要比您可能拥有的更多有关模式和保持空间的知识(并且这些知识通常不值得学习,因为用其他语言更容易做到)。

简而言之,您需要使用或者。例如:

$ perl -lne 'BEGIN { $, = "," };

             if (m=(\b\d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d{1,2}\b)=) {
               $dt = $1
             } elsif (m/(INC-\d+-\d+)/) {
               print $1, $dt;
               #$dt = ""; # uncomment to clear $dt before next input line
             }' input.txt 
INC-220721-00007628,8/1/2022 6:15

这个perl单行脚本使用perl的-l选项来启用自动处理行结束,同时读取输入和打印输出(例如\n在unix或\r\nwindows上),该-n选项使perl像sed -n.该-e选项指示下一个参数是要由 perl 运行的脚本。

首先,此脚本将输出字段分隔符 ( $,) 设置为逗号。该变量记录在曼·珀尔瓦尔。它在一个BEGIN {...}块中执行此操作,以便在脚本启动时仅运行一次,而不是为每个读取的输入行运行一次。

顺便说一句,您可以选择使用 perl英语如果您不喜欢或无法记住神秘的单字符变量名称(在use English;脚本内部或-MEnglish单行代码中),这可以让您访问长描述性英文别名和类似 awk 的等效项(如果适用)短变量。例如,use English您可以使用$,$OUTPUT_FIELD_SEPARATOR或类似 awk 的命令$OFS- 它们都表示相同的含义并引用相同的变量。

该脚本使用 Perl 的正则表达式匹配运算符m来匹配(并捕获,由于正则表达式中的括号)所需的模式。看曼·珀洛普并搜索“米/图案“。第一次使用 时m,我将=其用作正则表达式分隔符,这样我就不必转义/日期模式中的 s。第二次,我使用更熟悉的/.

另请参阅手册页佩尔雷, 也佩尔雷奎克佩尔雷图特

对于读取的每个输入行,它尝试匹配所需的日期和时间模式,如果成功,则存储捕获的日期和时间(来自 Perl 的$1子模式匹配变量,这类似于\1in sed- 查看man perlvar并搜索标题为“的部分”与正则表达式相关的变量") 到一个名为 的变量中$dt

如果之前的匹配不成功,它会尝试匹配该INC-\d+-\d+模式。如果成功,它将打印捕获的模式和$dt变量。

所有其他输入都将被忽略。

或使用 awk:

$ awk -v OFS=, '
  match($0,/\<[[:digit:]]{1,2}\/[[:digit:]]{1,2}\/[[:digit:]]{4} [[:digit:]]{1,2}:[[:digit:]]{1,2}\>/,a) {
    dt = a[0]; next
  };
  match($0,/INC-[[:digit:]]+-[[:digit:]]+/,a) {
    print a[0], dt
  }' input.txt 
INC-220721-00007628,8/1/2022 6:15

awk 有一个方便的-v选项来设置 awk 变量,因此我们不需要BEGIN块来设置 OFS。

这个 awk 单行语句几乎是 Perl 版本的直接翻译,但使用 awk 的match()函数来进行匹配和捕获测试。它将所有匹配捕获到 array 中a

它还使用[[:digit:]]与 perl 等效的\d.在许多语言环境中,您可以使用 using 来[0-9]代替,但是两者[[:digit:]]和 perl都\d可以在任何语言环境中工作。

答案3

grep -Eo 'INC-[[:digit:]]{6}-[[:digit:]]+|[[:digit:]]{1,2}/[[:digit:]]{1,2}/[[:digit:]]{4} [[:digit:]]{1,2}:[[:digit:]]{1,2}' inputfile.txt |
 tr '\n' ',' | 
 sed 's/,$//'

笔记:

  • grep:
    • -o: 只获取匹配的表达式
    • -E:接受扩展 RE
      • 模式由管道 ( |)、布尔运算符“或”分隔

结果是:

8/1/2022
INC-220721-00007628

该顺序与输入文件中的顺序相同。

  • tr ...: 将换行符 ( \n) 转换为逗号 ( ,)

结果是:

8/1/2022 6:15,INC-220721-00007628,
  • sed ...:搜索并替换/删除命令添加的最后一个tr逗号

结果是:

8/1/2022 6:15,INC-220721-00007628

答案4

Withpcregrep的多行模式:

$ pcregrep -M -o2 -o1 --om-separator=, '(?s) (\d+/\d+/\d+ \d+:\d+) .*?(INC-\d{6}-\d{8})' your-file
INC-220721-00007628,8/1/2022 6:15

相关内容