使用带有Awk的Bash脚本将动态TSV/CSV文件拆分为3个单独的文件

u5rb5r59  于 2023-09-28  发布在  其他
关注(0)|答案(3)|浏览(128)

我有一个动态csv/tsv文件(tab delimiter),每小时在 * 借方计数 * 和 * 分数计数 * 下面添加一个新行。* 入库次数 * 下不会新增行,只是数值每小时变化一次。请参见下面两个示例以供参考。
第3小时FileA.csv示例

Debit Count     VALUE
hour 1          5
hour 2          81
hour 3          15
Score Count
hour 1          31
hour 2          66
hour 3          9
Receipt Count
age logs        23
bus logs        21
pig logs        7
dog logs        40

文件A.csv示例第7小时

Debit Count     VALUE
hour 1          5
hour 2          81
hour 3          15
hour 4          20
hour 5          52
hour 6          33
hour 7          35
Score Count    
hour 1          31
hour 2          66
hour 3          9
hour 4          112
hour 5          15
hour 6          38
hour 7          21
Receipt Count  
age logs        13
bus logs        28
pig logs        85
dog logs        55

因此,我试图实现的是将FileA.csv分离为ABC.csv、DEF.csv和GHI.csv,请记住 *Debit Count * 和 Score Count 下面的行每小时都会增加。新文件ABC.csv、DEF.csv、GHI.csv将每小时被替换一次
使用第三个小时的例子来参考我试图实现的目标
ABC.csv

Debit Count     VALUE
hour 1          5
hour 2          81
hour 3          15

DEF.csv

Score Count  
hour 1          31
hour 2          66
hour 3          9

GHI.csv

Receipt Count
age logs        23
bus logs        21
pig logs        7
dog logs        40

我在做什么(编辑)

awk f="ABC.csv DEF.csv GHI.csv" '
  BEGIN {split(f,files)} /^Debit/ /^Score/ /^Receipt/ {n++} {print>files[n]}' FileA.csv
  • 此问题因previous post中缺乏焦点而关闭,我可以选择编辑或重新发布问题。我决定以更清晰的方式重新发布这个问题,以便以前可能见过它的其他人可以再次看到它。谢谢 *
ubbxdtey

ubbxdtey1#

当您到达每个标题行时,使用awk更改输出文件。

awk '/Debit Count/ { of="ABC.csv" }
     /Score Count/ { of="DEF.csv" }
     /Receipt Count/ { of="GHI.csv" }
     {print >of}' FileA.csv
ee7vknir

ee7vknir2#

这可能对你有用(GNU cplip):

csplit -szfX -n1 file '/^[DSR]/' '{*}' && mv X0 ABC.csv && mv X1 DEF.csv && mv X2 GHI.csv

根据标题的第一个字符分割文件。-z选项省略第一个空文件,-s使输出保持静默。其余的选项是不必要的,但可以构成一个简洁的一行程序。

n3ipq98p

n3ipq98p3#

您在“我尝试做的事情(编辑)”下的代码:

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

很接近,应该是:

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

或:

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

顺便说一下,你的输入文件既不是CSV,也不是TSV。它不是CSV,因为它不是逗号分隔的,它是制表符分隔的,而且两者都不是,因为这两种格式都只有一个标题行作为第一行,它们在文件中没有多个标题行。因此,它只是一个普通的旧文本文件,其中散布着一些标题,标题下有制表符分隔的名称-值对,因此您的输入文件应该命名为FileA.txt,而不是FileA.csvFileA.tsv
即使你的第二个输出文件也不是TSV,因为标题行只是一个关于文件的字符串,而不是文件中2列的名称-如果你将Receipt<blank>Count更改为Receipt<tab>CountReceipt Count<tab>Value或类似的名称,那么它将是有效的TSV,你可以将输出文件命名为GHI.tsv等。文件后缀对于其他人或后续工具理解其内容很重要。
实际上也不需要测试字符串Debit等。你只需要在每次$2为空时修改输出文件,我不认为用变量传递输出文件名比在脚本中将它们变成字符串有什么好处。
所以你的代码应该是:

$ awk '
    BEGIN {
        split("ABC.tsv DEF.tsv GHI.tsv",files)
        FS = OFS = "\t"
    }
    NR == 1  { hd=$2; n++ }
    $2 == "" { $2=hd; n++ }
    { print > files[n] }
' FileA.txt
$ head *.tsv
==> ABC.tsv <==
Debit Count     VALUE
hour 1  5
hour 2  81
hour 3  15
hour 4  20
hour 5  52
hour 6  33
hour 7  35

==> DEF.tsv <==
Score Count     VALUE
hour 1  31
hour 2  66
hour 3  9
hour 4  112
hour 5  15
hour 6  38
hour 7  21

==> GHI.tsv <==
Receipt Count   VALUE
age logs        13
bus logs        28
pig logs        85
dog logs        55

相关问题