根据其他文件中的字符串拆分文件

时间:2013-08-27 16:37:10

标签: linux bash file awk split

我有两个文件,我想将file1分成多个部分,这些部分等于file2中的行数。

此处file2中的每一行都将与file1进行比较,而file1中的所有匹配记录都将形成为新文件。

文件1

  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974096" STATUS="0" SQLTEXT="SHOW COLLATION"/> 
  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974099" STATUS="0" SQLTEXT="SHOW TABLES"/> 
  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974095" STATUS="0" SQLTEXT="SHOW COLLATION"/> 
  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974094" STATUS="0" SQLTEXT="SHOW COLLATION"/> 
  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974099" STATUS="0" SQLTEXT="SHOW COLLATION"/> 
  <AUDIT_RECORD TIMESTAMP="2013-08-26T19:31:17" NAME="Query" CONNECTION_ID="21974094" STATUS="0" SQLTEXT="SET NAMES utf8"/> 

file2的

21974096
21974100
21974095
21974094
21974099

必需的输出:

21974094.txt

==========================================
TIMESTAMP="2013-08-26T19:31:17"
SQLTEXT="SET NAMES utf8"

==========================================
TIMESTAMP="2013-08-26T19:31:17"
SQLTEXT="SHOW COLLATION"


21974099.txt

==========================================
TIMESTAMP="2013-08-26T19:31:17"
SQLTEXT="SHOW COLLATION"

==========================================
TIMESTAMP="2013-08-26T19:31:17"
SQLTEXT="SHOW TABLES"


21974095.txt

==========================================
TIMESTAMP="2013-08-26T19:31:17"
SQLTEXT="SHOW COLLATION"

...

为了用awk实现这一点,我已经编写了如下代码,但它不能满足我的要求。

awk 'NR==FNR{a[$4];next}!($4 in a){ print $2 "\n" $6 "\n=========\n" > $4 ".txt"}' file2  file1

任何人都可以帮助我用awk或任何其他shell命令来实现上述要求。 (它必须生成至少10000个文件,它应该在最多10分钟内完成生成文件。)

第一次尝试

如果我执行了以下命令,则几乎达到但不完全满足要求。

awk 'NR==FNR{a[$1];next}{split($4,b,"\"")}(b[2] in a){print $2"\n"$10"\n=========\n" > b[2]".txt"}' file2 file1

从其中一个文件输出

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="/*
=========

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="/*
=========

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="SHOW

但我想要OUTPUT如下

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="/*show variables"
=========

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="/* select * from table "
=========

TIMESTAMP="2013-08-26T19:57:34"
SQLTEXT="SHOW collations "

意味着我需要为file1指定分隔符为“”并且没有为file2分隔符。

对此有任何帮助吗?

3 个答案:

答案 0 :(得分:3)

您应该使用split函数将第四个单词与"字符分开,以便获得与file2中的值匹配的数字。您还应该在firth区域中a[$1]

此脚本应该有效:

awk 'NR==FNR{a[$1];next}{split($4,b,"\"")}(b[2] in a){print $2"\n"$6"\n=========\n" > b[2]".txt"}' file2  file1

更新:

只要file1中没有多余的引号,我们就可以使用"字符作为字段分隔符:

awk -F\" 'NR==FNR{a[$1];next}($6 in a){print "TIMESTAMP=\""$2"\"\nSQLTEXT=\""$10"\"\n=========\n" > $6".txt"}' file2  file1

我们将输入文件与"字符分隔为分隔符,因此字段$ 2是时间戳,字段$ 6是conn。 id,并提交10美元是SQLTEXT。

第一个块NR==FNR{a[$1];next}使用来自file2的连接ID填充数组(对于第一个文件,NR == FNR)。使用($6 in a)我们过滤第二个文件的行(因为我们在第一个块中调用了next),其连接id是表a的索引。如果我们找到匹配项,则执行块{print "TIMESTAMP=\""$2"\"\nSQLTEXT=\""$10"\"\n=========\n" > $6".txt"}',将相关信息打印到文件conn_id.txt

答案 1 :(得分:2)

这不会使用awk,但它有效:

while read -r n
do
    echo "Generating $n.txt"
    grep $n file1 | sed 's/^.*\(TIMESTAMP="[^"]\+"\).*\(SQLTEXT="[^"]\+"\).*$/=======================\n\1\n\2\n/' > $n.txt

    # If you don't want an empty file when there's no match, add this line
    if [ ! -s $n.txt ]; then rm -f $n.txt ; fi
done < file2

答案 2 :(得分:2)

这是我的解决方案:

#!/usr/bin/gawk -f

BEGIN {
    f = ARGV[2]
    while (getline id < f) {
        ids[id] = 0
    }
    ARGV[2] = ""
}

match($0, /.*<AUDIT_RECORD.* (TIMESTAMP="[^"]*").* CONNECTION_ID="([^"]*)".* (SQLTEXT="[^"]*").*/, a) {
    id = a[2]
    if (id in ids) {
        key = id "|" ids[id]++
        timestamps[key] = a[1]
        sqltexts[key] = a[3]
    }
}

END {
    for (id in ids) {
        count = ids[id]
        if (count) {
            file = id ".txt"
            for (i = 0; i < count; ++i) {
                key = id "|" i
                printf "%s\n%s\n%s\n\n", "==========================================", timestamps[key], sqltexts[key] > file
            }
            close(file)
        }
    }
}

使用

运行它

gawk -f script.awk file1 file2

我实际上更喜欢在输入第一个文件的循环之前预处理第二个文件,因为我不喜欢添加不必要的条件检查。

如果它不是您想要的确切输出,您也可以修改printf

实际上,在其他解决方案中,gawk的匹配可以允许一个具体确定哪些参数应该被定位,所以如果其他行的格式与其他额外的键/值对略有不同,上面的代码也可以工作。

<强>更新

这个更简单但有可能一次打开太多输出文件,具体取决于条目数。

#!/usr/bin/gawk -f

BEGIN {
    f = ARGV[2]
    while (getline id < f) {
        ids[id] = 0
    }
    ARGV[2] = ""
}

match($0, /.*<AUDIT_RECORD.* (TIMESTAMP="[^"]*").* CONNECTION_ID="([^"]*)".* (SQLTEXT="[^"]*").*/, a) {
    id = a[2]
    if (id in ids) {
        printf "%s\n%s\n%s\n\n", "==========================================", a[1], a[3] > id ".txt"
    }
}