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

ff29svar  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(85)

已关闭,此问题需要更focused。它目前不接受回答。
**想改善这个问题吗?**更新问题,使其只关注editing this post的一个问题。

上个月关门了。
Improve this question
我已经看到类似的问题,但似乎没有接近我试图实现的目标。
我有一个动态csv文件(制表符分隔符),每小时更新/追加一次但注意:只有HEADER 1* 和 HEADER 2 下面的行数每小时增加。请看下面两个例子作为参考
第3小时FileA.csv
示例**

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

文件A.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。
1.然后通过搜索Header名称并将其与其下的所有后续行一起剪切掉HEADER 2及以下
1.最后,剩下HEADER 1,我们可以将其留在FileA.csv中,也可以将其移到ABC.csv中
请帮忙,谢谢

vfhzx4xs

vfhzx4xs1#

对于任意awk和任意数量的文本块:

awk '/^HEADER/ {n++} {print>("File" n ".csv")}' FileA.csv

我们只需在每一行以HEADER开始时递增变量n(默认情况下初始化为0),然后打印所有重定向到名为Filen.csv的文件的行。
注意:如果其他行也可以以HEADER开头,则可以更具体地说明头部正则表达式(例如,/^HEADER [[:digit:]]+ NUM$/)。
输出文件名为File1.csvFile2.csv、...如果你绝对想要ABC.csvDEF.csvGHI.csv,你可以用途:

awk -v f="ABC.csv DEF.csv GHI.csv" '
  BEGIN {split(f,files)} /^HEADER/ {n++} {print>files[n]}' FileA.csv

说明:

  • 我们将空格分隔的文件名列表作为变量f传递。
  • 我们将其拆分为空格并将其存储在数组files中。
  • 打印时,我们不是重定向到文件Filen.csv,而是重定向到files数组的条目号n

请注意,如果你有更多的文本块比列出的文件,你会得到一个错误时,数组索引溢出。

neekobn8

neekobn82#

假设标题字面上有“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

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

i7uq4tfw

i7uq4tfw3#

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

test.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
  • 第三行- Same Thing
  • 当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文件,可以在脚本中FILE=FileA.csv行之前或之后写入rm ABC.csv DEF.csv, GHI.csv。这样,您总是可以获得全新的文件。

相关问题