sh 按匹配的列名值分解 CSV 文件,同时保留标题

2023-12-01

我有一个目录,其中包含来自表导出的许多 CSV 文件

tblA.csv

A,B,C
1,1,1
1,2,2
2,2,2
3,3,3

tblB.csv

C,D,A
1,1,1
1,2,2
2,2,2
3,3,3

为了破坏文件我找到了这个脚本

   awk -F, '
      NR== 1 { hdr = $0;next}
      {out = "File" $1 ".csv"}
      printed[$1]++<1 {print hdr >out}
      {print $0 > out}
  ' tblA.csv 

创建 3 个文件

A,B,C
1,1,1
1,2,2
A,B,C
2,2,2
A,B,C
3,3,3

现在,对于 tblB.csv,我仍然需要按 A 列分解文件,但该列是第三列而不是第一列

我无法找出传递参数 A 的方法,让它循环标题以查找与我传递的参数匹配的列名称,然后使用该列值中断文件。或者,如果该列名不存在,则跳过该文件。


以下是如何按列名称打印列:

按列的名称打印列:

$ cat tst.awk
BEGIN { FS="," }
NR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
}
{ print $(f["A"]) }

$ awk -f tst.awk tblA.csv
A
1
1
2
3

$ awk -f tst.awk tblB.csv
A
1
2
2
3

以下是如何使用该习惯用法在每个 Unix 机器上的任何 shell 中使用任何 awk* 来稳健而高效地完成您所要求的操作:

按分组键值拆分输入文件:

$ cat tst.awk
BEGIN { FS="," }
NR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
    hdr = $0
    next
}
!(tgt in f) { exit }
{ curr = $(f[tgt]) }
curr != prev {
    close(out)
    out = "File" curr ".csv"
    print hdr > out
    prev = curr
}
{ print > out }

$ awk -v tgt='A' -f tst.awk tblA.csv

$ head File*.csv
==> File1.csv <==
A,B,C
1,1,1
1,2,2

==> File2.csv <==
A,B,C
2,2,2

==> File3.csv <==
A,B,C
3,3,3

$ awk -v tgt='A' -f tst.awk tblB.csv

$ head File*.csv
==> File1.csv <==
C,D,A
1,1,1

==> File2.csv <==
C,D,A
1,2,2
2,2,2

==> File3.csv <==
C,D,A
3,3,3

上面假设输入文件按示例输入中所示的关键字段进行分组 - 如果不是,则可以在 awk 脚本中处理:

仅使用 AWK 按非分组键值拆分输入文件:

$ cat tblC.csv
C,D,A
2,2,3
1,2,2
1,1,3
3,3,1

$ cat tst.awk
BEGIN { FS="," }
NR==1 {
    for (i=1; i<=NF; i++) {
        f[$i] = i
    }
    hdr = $0
    next
}
{ curr = $(f[tgt]) }
curr != prev {
    close(out)
    out = "File" curr ".csv"
    if ( !doneHdr[curr]++ ) {
        print hdr > out
    }
    prev = curr
}
{ print >> out }

$ awk -v tgt='A' -f tst.awk tblC.csv

$ head File*.csv
==> File1.csv <==
C,D,A
3,3,1

==> File2.csv <==
C,D,A
1,2,2

==> File3.csv <==
C,D,A
2,2,3
1,1,3

但如果您的文件很大,对它们进行排序会更有效,因此在运行 awk 脚本之前对键值进行分组,这样 awk 就不必重复打开/关闭输出文件:

使用 sort+AWK 按非分组键值拆分输入文件(对于大文件更有效):

$ cat tst.sh
#!/usr/bin/env bash

tgt="$1"
shift

awk -v tgt="$tgt" '
    BEGIN { FS=","; OFS="\t" }
    NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
    }
    { print (NR>1), $(f[tgt]), NR, $0 }
' "${@:--}" |
sort -k1,1n -k2,2 -k3,3n |
cut -f4- |
awk -v tgt="$tgt" '
    BEGIN { FS="," }
    NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
        hdr = $0
        next
    }
    { curr = $(f[tgt]) }
    curr != prev {
        close(out)
        out = "File" curr ".csv"
        print hdr > out
        prev = curr
    }
    { print > out }
'

$ ./tst.sh 'A' tblC.csv

$ head File*.csv
==> File1.csv <==
C,D,A
3,3,1

==> File2.csv <==
C,D,A
1,2,2

==> File3.csv <==
C,D,A
2,2,3
1,1,3

上面的工作原理是首先使用 awk 通过在每一行前面添加来装饰原始输入:

  1. NR>1= 标头或非标头、0 或 1 指示符,这样我们就可以确保标头始终在sort,
  2. $(f[tgt])= 我们要排序的键值,
  3. NR= 当前行号,因此我们得到与重复键输入相同的顺序输出(也可以不添加它,然后使用 GNU 排序来完成-s)

然后我们按这些字段排序,然后使用再次删除它们cut(可以在后续的 awk 脚本中执行此操作,但是cut在主 awk 脚本开始创建输出文件之前,它是高效的并且避免混乱)。

此类 AWK 脚本中需要注意的事项:

* 如果您得到执行以下任一操作的其他答案,请注意:

  1. 任何不支持的解决方案close()一旦超过可能低至 15 的阈值,大多数 awk 中的输出文件都会因“打开文件过多”而失败,甚至支持无限“打开”文件(例如 GNU awk)的 awk 也会减慢超过该阈值的速度,如下所示它必须通过根据需要在幕后实际打开/关闭操作系统来管理所有这些“打开”文件,并且
  2. 任何使用的解决方案print > "File" $1 ".csv"或类似的没有 将右侧的表达式括起来>在大多数 awks 中都会因语法错误而失败,因为 未定义的行为。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

sh 按匹配的列名值分解 CSV 文件,同时保留标题 的相关文章

随机推荐

  • 就像 MySQL 中区分大小写一样

    我有一个 MySQL 查询 SELECT concat ws title description as concatenated HAVING concatenated LIKE SearchTerm 我的表是用 MyISAM 编码的 ut
  • 使用原始音乐数组进行循环 android

    我目前正在尝试创建一个 for 循环 在该循环中它将播放原始文件 完成后 它将继续播放数组中的下一个声音文件 目前它正在一次播放所有文件 关于如何解决这个问题有什么建议吗 我认为完成监听器可能有问题 但不确定 谢谢 my Code pack
  • 使用单个 URL 抓取多个页面上的表

    我正在尝试从 Fangraph 中抓取数据 这些表分为 21 个页面 但所有页面都使用相同的 URL 我对网络抓取 或一般的Python 非常陌生 但Fangraphs没有公共API 所以抓取页面似乎是我唯一的选择 我目前正在使用 Beau
  • Javafx 四边形网格

    我需要在javafx中显示一个四边形网格 每个网格面有4个点我尝试了一些来自fxyz的三角形网格示例library 但不确定它如何适用于四边形 有人可以帮助指出 javafx 中四边形网格的示例吗 OpenJFX 上提供的 3DViewer
  • 如果我在不同的国家/地区,如何知道特定国家/地区的时间?

    我正在尝试这样做 1 获取我的当前时间和时区 2 我从 Google 知道该特定国家 地区的时区 3 计算时区差异 4 从当前时间中减去该差值 这会给我在其他国家的时间 我被困在步骤 3 和 4 了 我得到这样的当前时间 Date d ne
  • Java字符串操作:根据模式从字符串中提取整数和浮点数

    我有以下两个可能的字符串内容 显然 金额总是有所不同 我想提取关键信息并 Case 0 pricesString Case 1 pricesString 0 023 Case 2 pricesString 10 1 46 100 0 16
  • Oracle:发送 JMS 消息的 Java 存储过程

    我正在尝试将点对点 JMS 消息从 Oracle 数据库存储过程发送到 java 应用程序 这两个 点 位于不同的机器上 我已确认它们可以通过 ping 相互通信 我创建了一个 java 应用程序 能够成功地从应用程序服务器内的队列中取出消
  • Android Studio 中未显示 Windows 的构建选项

    我使用的是 Flutter Desktop 的最新稳定版本 并且已经安装了 Visual Studio 2022 但仍然没有显示 Windows 选项 我还需要在稳定版本中运行以下命令吗 flutter config enable
  • 删除 NaN 行在 pandas 中不起作用[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 目前不接受答案 我有一个大约 7k 行和 4 列的文件 很多单元格都是空的 我尝试使用许多 pandas 函数删除它们 但似乎没有任何效果 我尝试过的功能和代码如下 我尝试过的 df df
  • 为什么 GetProcAddress 不起作用?

    首先 我创建一个简单的 dll 名为SimpleDll dll 其头文件 SimpleDll h ifdef MYLIBAPI else define MYLIBAPI declspec dllimport endif MYLIBAPI i
  • javascript 中函数闭包前是否需要分号?

    我想知道这是否是一个编译器错误 或者这就是它应该的方式 使用node js v10 15 3 此代码会抛出错误 var x x false function y console log foo 但这段代码工作正常 var x x false
  • 隐藏/不可见的 Ajax 请求?

    是否可以编写和创建一个无法被 Firefox 中的 Firebug 插件捕获的 JavaScript Ajax 请求 我问这个问题是因为我可以在 Facebook 上看到没有正在进行的 Ajax 请求 但是当我从另一个帐户发送消息时 顶部的
  • 2D CUDA 中值滤波器优化

    我在 CUDA 中实现了一个 2D 中值滤波器 整个程序如下所示 include cuda runtime h include cuda runtime api h include device launch parameters h in
  • MiniZinc 数组中字符串值的索引

    问题 给定一个 MiniZinc 字符串数组 int numStats set of int Stats 1 numStats array Stats of string statNames 使用从 MiniZinc 数据文件加载的数据 n
  • 无法安装 NuGet 包 - 500 内部服务器错误

    这对我来说毫无意义 NuGet 以前工作正常 但现在当我尝试安装软件包时它会抛出服务器错误 我已经卸载并重新安装了最新的 NuGet 但仍然没有成功 PM gt Install Package EntityFramework Install
  • 为什么 LocationManager 没有 LastKnown 位置?

    我想要用户的位置 并且在该用户自己导航后也只需要一次 locationManager LocationManager this getSystemService LOCATION SERVICE location locationManag
  • 如何在 Windows 7 上安装 Windows Phone 8 SDK

    我在 Windows 7 上设置了所有工作区和所有内容 但我也想开发 Windows Phone 8 但正如 Microsoft 网站所述 它无法安装在 Windows 7 上 有人知道如何在 Windows 7 上安装吗 我找到了一个破解
  • iOS Ionic 应用程序中的链接无法立即打开

    我的应用程序的视图之一中有一些链接 Sharing 添加到日历 打开外部链接 在我用来测试应用程序的 iPhone 上 1 和 2 可以工作 但只有当我按下主页按钮然后返回到应用程序时 才会出现共享 日历对话框 谁能建议什么可能导致这个 我
  • 使用对象类型的字符串名称在 C# 中进行类型转换

    我有以下代码 应该很容易理解 public class Foo public void FooHasAMethod Console WriteLine it is me foo public class Bar public Foo Foo
  • sh 按匹配的列名值分解 CSV 文件,同时保留标题

    我有一个目录 其中包含来自表导出的许多 CSV 文件 tblA csv A B C 1 1 1 1 2 2 2 2 2 3 3 3 tblB csv C D A 1 1 1 1 2 2 2 2 2 3 3 3 为了破坏文件我找到了这个脚本