【问题标题】:Split a file based on the string from the other file根据来自另一个文件的字符串拆分文件
【发布时间】:2013-08-30 12:48:56
【问题描述】:

我有两个文件,我想将 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"/> 

文件2

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

但我想要如下所示的输出

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 "

这里的意思是我需要将文件 1 的分隔符指定为 '"' 而文件 2 没有分隔符..

有人可以帮忙吗?

【问题讨论】:

  • 你可以使用 GNU 版本的 awk 吗?
  • 如果file2中的数字在file1中不存在,你想怎么办?
  • 数字总是存在的......因为从file1中提取的数字是通过一些过滤得到的。
  • 这个过程是运行一次,还是必须定期运行?
  • 这是要保存在 cron 中的东西

标签: linux bash file awk split


【解决方案1】:

这不使用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

【讨论】:

  • grep 和 sed 都很昂贵,而且它们都在循环中......哦,我的!..每次它必须读取文件并进行整个文件扫描......这里的速度非常快正如我在我的问题中已经提到的那样重要..那么你能帮我解决这个问题吗?没有 shell 脚本循环......
  • @vidyadhar 我同意这些与其他解决方案相比成本高昂。您最初的问题陈述表明您有很多文件要做,但您说“使用 awk 或任何其他 shell 命令”。您表示“不到 10 分钟”,但未指明文件大小,并且此 shell 方法可能会在 10 分钟内完成,具体取决于文件大小。为了获得最佳性能,我建议使用 Python 等脚本语言编写。您熟悉哪些语言?
  • 感谢您的回复,文件大小约为 500 MB....而且我基本上不是程序员...但我对 java 和 c,c++,perl 不太熟悉...但在这里我已经通过 shell 脚本或 sed、awk 等 shell 命令实现了这一点......
  • 对于输入文件的各种内容和/或运行它的目录等,该脚本会神秘地失败。awk 被发明用于解析文本文件 - 只需使用正确的工具工作。
  • @EdMorton 我同意awk 是解决此问题的最佳解决方案。该解决方案已被 OP 接受。
【解决方案2】:

您应该使用 split 函数将第四个单词用 " 字符分隔,这样您就可以得到与 file2 中的值匹配的数字。您还应该在第 5 个区块中 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" &gt; $6".txt"}',将相关信息打印到文件conn_id.txt

【讨论】:

  • 你不应该看到任何输出。但是应该使用示例中的测试文件创建四个文件21974094.txt 21974095.txt 21974096.txt 21974099.txt
  • 是的,他们来了。 :)
  • 感谢您的解决方案...但它不完整请参考我刚刚编辑的问题.....
  • 不...我认为这只是因为这里的两个文件都使用了指定的定界符...我们必须将其用作仅 file1 的定界符,因为 file2 内部没有'"'它......有什么帮助吗?
  • 非常感谢......它正在工作......只是我这边的一个小错误......请您在上面添加解释......请......
【解决方案3】:

这是我的解决方案:

#!/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"
    }
}

【讨论】:

  • 谢谢...请给我推荐一本书来学习 gawk 脚本...我对它完全陌生...
  • @vidyadhar 实际上我刚刚通过阅读 Gawk 的手册学习了 Awk 脚本:gnu.org/software/gawk/manual/gawk.html。而且我每次编码时都会参考man gawk
  • 您提供的上述代码很好理解,也很简单......但是生成文件需要20多分钟......有什么帮助吗?
  • 与其他脚本相比,该脚本具有更谨慎的方法,因此它自然很慢。它首先将数据存储在数组中,正确匹配关键模式,一次打印特定文件的数据,并确保一次打开的输出文件句柄不超过一个。这是它可能具有的最佳形式,否则它将具有与此线程上共享的其他脚本相同的概念。
  • @vidyadhar 好的,我制作了一个更简单的版本,它跳过了存储数据,但请考虑 Awk 一次打开多个文件的可能性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 2013-06-06
  • 1970-01-01
相关资源
最近更新 更多