使用 Bash 脚本与 Awk、Sed、Grep 等将动态 CSV 文件拆分为 3 个单独的文件 [已关闭]

问题描述 投票:0回答:3

我到处都看到过类似的问题,但似乎没有一个接近我想要实现的目标。

我有一个动态 csv 文件(制表符分隔符),每小时都会更新/附加 但注意: 只有 HEADER 1HEADER 2 下面的行数每小时都会增加。请看下面两个例子作为参考

FileA.csv 示例 第 3 小时

HEADER 1 NUM
hour 1   5
hour 2   10
hour 3   15
HEADER 2 NUM
hour 1   3
hour 2   6
hour 3   9
HEADER 3 NUM
age      23
bus      21
pig      07
dog      40

FileA.csv 示例第 7 个小时

HEADER 1 NUM
hour 1   5
hour 2   10
hour 3   15
hour 4   20
hour 5   25
hour 6   30
hour 7   35
HEADER 2 NUM
hour 1   3
hour 2   6
hour 3   9
hour 4   12
hour 5   15
hour 6   18
hour 7   21
HEADER 3 NUM
age      13
bus      28
pig      85
dog      55

标题 1 和标题 2 下面的行每小时都会增加。标题 3 及以下是唯一保持不变的内容

所以我想要实现的只是将 FileA.csv 分成 ABC.csv , DEF.csv , GHI.csv

使用第三个小时的例子来参考我想要实现的目标

ABC.csv

HEADER 1 NUM
hour 1   5
hour 2   10
hour 3   15

DEF.csv

HEADER 2 NUM
hour 1   3
hour 2   6
hour 3   9

GHI.csv

HEADER 3 NUM
age      23
bus      21
pig      07
dog      40

下面是我尝试使用 grep 执行的操作,但我无法结合 grep 和 cut 来实现此目的。我尝试过使用 Sed 但不知道如何在搜索后剪切和移动它。我知道这可以用 awk 实现,但 awk 并不强大

  1. 首先剪下 HEADER 3 和下面的后续行,并将其放入 GHI.csv 中,因为它始终是不变的,这样我们就只剩下 HEADER 1 和 HEADER 2。
  2. 然后通过搜索标题名称并将其连同其下面的所有后续行一起剪切来剪切标题 2 及以下
  3. 最后我们留下 HEADER 1,我们可以将其保留在 FileA.csv 中或将其移至 ABC.csv

请帮忙。谢谢

bash csv awk sed grep
3个回答
1
投票

假设标题字面上有“HEADER ...”行,如上所述, 你能尝试一下吗:

awk '
    BEGIN {                     # define filenames to write
        fname[1] = "ABC.csv"; fname[2] = "DEF.csv"; fname[3] = "GHI.csv"
    }
    /^HEADER/ {                 # reached the header line
        if (c >= 1) close(file) # close the previous file, if opened
        file = fname[++c]       # update the filename to write
    }
    {
        print > file            # append to the file
    }
' FileA.csv

顺便说一句,文件正在增长的事实似乎与 文件分割。


0
投票

你可以写这样的东西。您不需要 awk、sed 或 grep。 Bash 本身可以为你做到这一点。

测试.sh

#!/bin/bash

FILE=FileA.csv

OUTPUT=ABC.csv
while read CMD; do
    
    if [[ "$CMD" == HEADER*1*NUM ]]; then
    OUTPUT=ABC.csv
    elif [[ "$CMD" == HEADER*2*NUM ]]; then
    OUTPUT=DEF.csv
    elif [[ "$CMD" == HEADER*3*NUM ]]; then
    OUTPUT=GHI.csv
    fi

    echo "$CMD" >> $OUTPUT

done < "$FILE"

echo "Done"

让我们运行一下吧

chmod 755 test.sh
./test.sh

结果文件

ABC.csv

HEADER 1 NUM
hour 1   5
hour 2   10
hour 3   15

DEF.csv

HEADER 2 NUM
hour 1   3
hour 2   6
hour 3   9

GHI.csv

HEADER 3 NUM
age      23
bus      21
pig      07
dog      40

说明

我们循环遍历文件的每一行。如果我们看到 HEADER 1 NUM,我们就说这些行应该写入 ABC.csv。如果该行有 HEADER 2 NUM,我们说这些行应该写入 DEF 等等。

然后我们将这些行写入相应的文件。

例如

  • 我们读了第一行。它有 HEADER 1 NUM,它与正则表达式 HEADER1NUM 匹配。所以,我们说输出文件应该是 ABC.csv
  • 然后,我们回显该行(存储在 CMD 变量中)并将其发送到输出文件,我们称其为 ABC.csv。
    >>
    表示附加到 ABC.csv 文件。因此,HEADER 1 NUM 被写入该文件
  • 然后,我们读第二行。没有一个 if..elif..elif..fi 语句与下一行匹配。因此,下一行将被回显并附加到 ABC.csv
  • 第三行 - 同样的事情
  • 当出现 HEADER 2 NUM 的行时,第一个 elif 满足该条件,并且输出文件更改为 DEF.csv
  • HEADER 2 NUM 行写入 DEF.csv
  • 接下来的行写入 DEF.csv
  • 持续进行,直到 HEADER 3 NUM 行与第二个 elif 匹配。此时输出文件将更改为 GHI.csv
  • HEADER 3 NUM 写入 GHI.csv
  • 后续行也写入 GHI.csv

如果要删除 ABC、DEF 和 GHI 文件,可以在脚本中的

rm ABC.csv DEF.csv, GHI.csv
行之前或之后写入
FILE=FileA.csv
。这样,您始终会获得全新的文件。


0
投票

如果您不关心确切的输出文件名并且您有 GNU

awk
,您可以尝试以下操作:

awk 'BEGIN {RS = "HEADER[^\n]*[\n]"}
  NR>1 {printf("%s%s",p,$0)>"File" NR-1 ".csv"} {p=RT}' FileA.csv

输出文件名称为

File1.csv
File2.csv
、... 如果您绝对想要
ABC.csv
DEF.csv
GHI.csv
,您可以使用:

awk -v f="ABC.csv DEF.csv GHI.csv" '
  BEGIN {RS = "HEADER[^\n]*[\n]"; split(f,files)}
        NR>1 {printf("%s%s",p,$0)>files[NR-1]} {p=RT}' FileA.csv
© www.soinside.com 2019 - 2024. All rights reserved.