Grep 查找从 $START 到 $END 并且在 $MIDDLE 中包含匹配项的一组行

Grep 查找从 $START 到 $END 并且在 $MIDDLE 中包含匹配项的一组行

Grep/Awk/Sed 用于查找“0010|”中的一组行至“0070|” AND 包含 $PH_NO 中的匹配项

以下是示例数据。我需要 grep 查找 0012 字段中出现的电话号码以及相应的完整客户记录(从 0010 到 0070 的行)。一个数据文件可能包含两到三个具有相同电话号码的客户记录,我需要获取所有这些记录。

0010|Kumar||57 Rich street|Chennai|Tamil Nadu|
0011|20171115| ID
0012|149 196 222| PH Number 
0013|20161101|20171102|
0022|Payment Method |Lucky customer|
0080|P|5.00-|20161111|Payment|
0080|P|5.00-|20161130|Payment|
0080|TP|10.00-|||
0070|000AYDCHDFF|820|762|
0010|RAM||57 Rich street|Chennai|Tamil Nadu|
0011|20171115| ID
0012|149 196 333| PH Number 
0013|20161101|20171102|
0022|Payment Method |Lucky customer|
0080|P|5.00-|20161111|Payment|
0080|P|5.00-|20161130|Payment|
0080|TP|10.00-|||
0070|000AYDCHDFF|820|762|
0010|Joe||57 Rich street|Chennai|Tamil Nadu|
0011|20171115| ID
0012|149 196 222| PH Number 
0013|20161101|20171102|
0022|Payment Method |Lucky customer|
0080|P|5.00-|20161111|Payment|
0080|P|5.00-|20161130|Payment|
0080|P|5.00-|20161111|Payment|
0080|P|5.00-|20161130|Payment|
0080|P|5.00-|20161111|Payment|
0080|P|5.00-|20161130|Payment|
0080|TP|10.00-|||
0070|000AYDCHDFF|820|762|

注意:我使用的是带有 ksh 的 AIX 服务器。

答案1

那么,如果 PH 编号匹配,您想要从 0010 到 0070 的整个记录$PH_NO"​​吗?然后这个sedoneliner 就可以工作了:

sed "/^0010/,/^0070/H;/^0010/h;/^0070/! d;x;/|$PH_NO| PH Number/! d"
  • /^0010/,/^0070/H将 0010 到 0070 之间的一条记录追加到保留空间
  • /^0010/h0010 不应被附加,而是开始一个新记录,因此将其复制到保留空间
  • /^0070/! d除非是 0070 项,否则不会进行进一步处理或输出
  • x;/|$PH_NO| PH Number/! d"交换空格,因此整个记录现在都在模式空间中,如果不包含所述数字则将其删除。

答案2

for r in `grep -n '^0010\|^0012\|^0070' CUSTOMER_FILE | grep -C1 '[0-9]\+:0012|149 196 222|' | grep -o '^[0-9]\+' | paste -d, - - - | sed 's/,[0-9]\+,/,/g'`; do sed -n "$r"p CUSTOMER_FILE; echo; done

149 196 222上面的命令中是客户的电话号码。将其更改为您要查找的电话号码。

CUSTOMER_FILE是您要搜索的文件。将其更改为您的文件名。

您还可以将代码放入 bash 脚本中,然后替换149 196 222$1和 替换CUSTOMER_FILE$2。说find-customer.sh,然后你可以像这样执行脚本

./find-customer.sh '149 196 222' your-file-name

这段代码的前提条件:

  1. Bash、GNU 环境(GNU grep、GNU sed)
  2. 您的文件必须遵循如下格式 0010 ... <no 0010 or 0012 or 0070> ... 0012 ... <no 0010 or 0012 or 0070> ... 0070 ... <repeated content as above or end of file>


更新

这是一个高性能版本。 (至少比上面原来的要高。完全不涉及for循环。)

grep -n '^0010\|^0012\|^0070' CUSTOMER_FILE | grep -C1 '[0-9]\+:0012|149 196 222|' | grep -o '^[0-9]\+' | paste -d, - - - | sed -r 's|([0-9]+),[0-9]+,([0-9]+)|\1,\2p;\2a|g' | sed -n -f - CUSTOMER_FILE


AIX 的更新

由于提问者正在 AIX 上工作。 AIX 上的 grep 不支持上下文选项 -A、-B、-C。

在Internet上,有多种“cgrep”(上下文grep)实现来解决这个问题(模拟GNU grep上下文选项)。但它们中的大多数不能提供与 GNU grep 相同的输出。我发现只有一个最接近 GNU grep 上下文选项。链接是https://stackoverflow.com/questions/1685678/advanced-grep-unix/1685782#1685782

我针对这个案例做了一些必要的修改。

#!/bin/bash
BEFORE=$1
AFTER=$1
FILE=/tmp/.cattmp
PATTERN="$2"
cat > $FILE
for i in $(grep -n "$PATTERN" $FILE | sed -e 's/\:.*//')
  do head -n $(($AFTER+$i)) $FILE | tail -n $(($AFTER+$BEFORE+1))
done
rm $FILE

将此文件另存为grep-context.sh并替换grep -C1./grep-context.sh 1上面我的命令中的内容。

我认为一劳永逸的另一种方法是在 AIX 上编译 GNU grep。 (也编译 GNU sed 以防万一)

答案3

您可以使用这个脚本:

#!/bin/sh
read START
read END
read MATCH

REND=$(grep -n "$END" lines | tail -1 | cut -d":" -f 1)
RSTART=$(grep -n "$START" lines | head -1 | cut -d":" -f 1)

sed $RSTART,$REND!d lines | grep "$MATCH"

将其放入文件中并使用此命令添加执行权限

chmod +x script.sh

启动变量:您的起始行号(例如 0010)

结束变量:您的结束变量(例如 0070)

匹配变量:您在一行中查找的单词/字符/数字(例如 0012)

RSTART 变量:文本文件中的起始行号(例如1)

撕裂变量:文本文件中的起始行号(例如 32)


编辑:

您还可以通过将最后一行更改为以下内容来查看一行重复了多少次:

sed $RSTART,$REND!d lines | grep "$MATCH" | sort | uniq -c

相关内容