SED或AWK脚本从大文件

时间:2018-02-07 21:30:02

标签: bash awk sed

我想通过提取重要的联系电话来处理包含学生信息的巨大文本文件。

情景:

我在students.txt中有学生记录(> 100万)。在此文件(每行)中

  • 字符1到10 - 学生证(长度10)
  • 字符11 - 成绩(长度1)
  • 字符12至21 - 手机号码(长度10)
  • 字符22至24 - 货币代码(长度3)
  • 字符25至34 - 家庭电话号码(长度10)
  • 第35至37号字符 - 区号(长度3)
  • 字符38至47 - 办公室电话号码(长度10)

等等

students.txt

1000000001A9234567890XXX5782463562...
1000000002B9325788532YYY...
1000000001C9234567890XXX6478542698XDE4578954568...
.
.
.
10010000008Z766443367ZZZ...

我想根据成绩

提取联系电话
  

A级 - 提取字符12至21 - 手机号码(长度10)和字符25至34 - 家庭电话号码(长度10)

     

B级 - 仅提取手机号码第12至21号字符 - 手机号码(长度为10)

     

C级 - 提取字符12至21 - 手机号码(长度10)和字符25至34 - 家庭电话号码和字符38至47 - 办公室电话号码(长度10)

不要为其他年级提取任何东西。

我希望将提取的输出放在一个具有唯一联系号

的文件中

输出:important_contacts.txt

9234567890
5782463562
9325788532
6478542698
4578954568

有人可以帮我解决这个问题吗?

注意:每个 students.txt 包含超过 250万条记录

3 个答案:

答案 0 :(得分:0)

这是如何做到这一点:

echo '1000000001A9234567890XXX5782463562... 
1000000002B9325788532YYY... 
1000000001C9234567890XXX6478542698XDE4578954568... ' | sed -n -r 's/[0-9]{10}A([0-9]{9}).*/\1/p'
923456789

或使用文件:

sed -n -r 's/[0-9]{10}A([0-9]{9}).*/\1/p' file > gradeA.txt 
  • sed -n表示不匹配的输出。
  • -r激活扩展正则表达式
  • s /.../.../用第2部分替换第1部分
  • [0-9]描述了作为密码的字符
  • {10}表示我们需要10个密码
  • A只是A级
  • (...)是一个捕获组,第一个,后来用\ 1
  • 引用
  • 它是数字,长度为9
  • 。*匹配任何内容,此处:行的其余部分
  • 缺少:要跳过的字符,第二个数字的定义。

每个Grade编写一个这样的程序来提取和输出到不同的文件。

百万行不是sed的问题。

答案 1 :(得分:0)

您可以传递输入文件" students.txt"到shell脚本,它逐行读取记录并根据等级解析数据。然后通过shell参数扩展' $'。

提取你想要的子串

如果不了解特定数据集的确切性质,请考虑以下输入文件 students.txt

StudentID123GradeAPhonenumber123
StudentID456GradeBPhonenumber456
StudentID789GradeCPhonenumber789
StudentID321GradeAPhonenumber312
StudentID654GradeBPhonenumber654
StudentID987GradeCPhonenumber987

此脚本 parse_grades.sh

#!/bin/sh

while read -r line; do
    # save a line
    PERSON=$line

    # extract the grade
    GRADE=${PERSON:17:1}

    # and now process it
    if [ "$GRADE" = "A" ]; then
        # show student ID and grade
        DATA_GRADE_A=${PERSON:0:18}
        echo $DATA_GRADE_A >> outputfile.txt
    fi
    if [ "$GRADE" = "B" ]; then
        # only show phone number
        DATA_GRADE_B=${PERSON:18:14}
        echo $DATA_GRADE_B >> outputfile.txt
    fi
    if [ "$GRADE" = "C" ]; then
        # show StudentID and PhoneNo
        SID=${PERSON:0:12}
        PHONENO=${PERSON:18:14}
        DATA_GRADE_C=$SID$PHONENO
        echo $DATA_GRADE_C >> outputfile.txt
    fi
done < "$1"

exit 0

使用以下命令使脚本可执行:

chmod +x parse_grades.sh

并将输入文件传递给它:

parse_grades.sh students.txt

根据以上记录集,您应该在outputfile.txt中获得以下结果:

StudentID123GradeA
Phonenumber456
StudentID789Phonenumber789
StudentID321GradeA
Phonenumber654
StudentID987Phonenumber987

脚本的核心思想是使用$ {VAR:Offset:Length}

现在您需要做的就是根据您的具体要求调整数字。

检查

man bash

或本网站了解更多信息

http://tldp.org/LDP/abs/html/string-manipulation.html

答案 2 :(得分:0)

使用 GNU awk,您可以使用内置的FIELDWIDTHS变量从固定宽度数据中提取字段值。以下单行执行您想要的任务:

awk 'BEGIN { FIELDWIDTHS = "10 1 10 3 10 3 10" } { if ($2 == "A") print $3 ORS $5; else if ($2 == "B") print $3; else if ($2 == "C") print $3 ORS $5 ORS $7; }' students-3M.txt > numbers.txt

您可以提供以空格分隔的数字,指定记录中每个字段的宽度:FIELDWIDTHS = "10 1 10 3 10 3 10"。 我们在读取任何输入记录之前在BEGIN规则中执行此操作。 然后,可以使用if else if构造根据您的规则打印所需的字段(列)。 对于在新行上需要多个电话号码的成绩,我们使用内置输出记录分隔符ORS打印我们的字段,默认情况下这是新行。

您可以将以下脚本保存到文件中,例如get-contacts.awk

BEGIN { FIELDWIDTHS = "10 1 10 3 10 3 10" }
{ 
    if ($2 == "A") {
        print $3 ORS $5
    } else if ($2 == "B") {
        print $3
    } else if ($2 == "C") {
        print $3 ORS $5 ORS $7
    }
}

并致电:

awk -f get-contacts.awk students-3M.txt > numbers.txt

我使用以下php脚本生成了3M行 测试数据

<?php

for ($i=0; $i < 3000000; $i++) { 
    $gr = strtoupper(substr(md5(microtime()),rand(0,26),1));
    $pn = rand(1,9999999999);
    $hn = rand(1,9999999999);
    $on = rand(1,9999999999);
    printf("100%07d%s%010dXXX%010dXDE%010d\n",$i,$gr,$pn,$hn,$on);
}
相关问题