以下是如何按列名称打印列:
按列的名称打印列:
$ 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 通过在每一行前面添加来装饰原始输入:
-
NR>1
= 标头或非标头、0 或 1 指示符,这样我们就可以确保标头始终在sort
,
-
$(f[tgt])
= 我们要排序的键值,
-
NR
= 当前行号,因此我们得到与重复键输入相同的顺序输出(也可以不添加它,然后使用 GNU 排序来完成-s
)
然后我们按这些字段排序,然后使用再次删除它们cut
(可以在后续的 awk 脚本中执行此操作,但是cut
在主 awk 脚本开始创建输出文件之前,它是高效的并且避免混乱)。
此类 AWK 脚本中需要注意的事项:
* 如果您得到执行以下任一操作的其他答案,请注意:
- 任何不支持的解决方案
close()
一旦超过可能低至 15 的阈值,大多数 awk 中的输出文件都会因“打开文件过多”而失败,甚至支持无限“打开”文件(例如 GNU awk)的 awk 也会减慢超过该阈值的速度,如下所示它必须通过根据需要在幕后实际打开/关闭操作系统来管理所有这些“打开”文件,并且
- 任何使用的解决方案
print > "File" $1 ".csv"
或类似的没有
将右侧的表达式括起来>
在大多数 awks 中都会因语法错误而失败,因为
未定义的行为。