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"
吗?然后这个sed
oneliner 就可以工作了:
sed "/^0010/,/^0070/H;/^0010/h;/^0070/! d;x;/|$PH_NO| PH Number/! d"
/^0010/,/^0070/H
将 0010 到 0070 之间的一条记录追加到保留空间/^0010/h
0010 不应被附加,而是开始一个新记录,因此将其复制到保留空间/^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
这段代码的前提条件:
- Bash、GNU 环境(GNU grep、GNU sed)
- 您的文件必须遵循如下格式
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