Bash 使用sed或awk对CSV文件进行排序和拆分
我有一个CSV文件test.CSV,如下所示:Bash 使用sed或awk对CSV文件进行排序和拆分,bash,awk,sed,Bash,Awk,Sed,我有一个CSV文件test.CSV,如下所示: WH_01,TRAINAMS,A10,1221-ESD WH_03,TRAINLON,L10A3,3005-21 WH_01,TRAINAMS,A101,PWR-120 WH_02,TRAINCLE,A1,074-HD-SATA WH_01,TRAINAMS,A10,PWR-120 WH_02,TRAINCLE,A15,102-55665 WH_03,TRAINLON,L10A3,3005-20 WH_03,TRAINLON,UK-B3,10185
WH_01,TRAINAMS,A10,1221-ESD
WH_03,TRAINLON,L10A3,3005-21
WH_01,TRAINAMS,A101,PWR-120
WH_02,TRAINCLE,A1,074-HD-SATA
WH_01,TRAINAMS,A10,PWR-120
WH_02,TRAINCLE,A15,102-55665
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,UK-B3,101859
一,。我可以根据第2列中的值对文件进行排序,如下所示:
sort -t, -k2,2 test.csv > testsort.csv
二,。接下来,我想根据第2列中的值拆分文件。使用上述示例,它应创建3个文件:
testsort_1.csv:
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A101,PWR-120
WH_01,TRAINAMS,A10,PWR-120
testsort_2.csv:
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665
testsort_3.csv:
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859
我该怎么做?不确定是否需要排序,以及是否可以在不进行排序的情况下实现上述功能
谢谢。您可以用一种相当简单的方式来完成,方法是为文件名保留一个计数器,并使用sprintf为每个连续的文件组创建文件名。您可以使用FNR文件记录编号来区分第一个记录和后续记录 例如:
$ sort -t, -k2 file.csv |
awk -F, -v cnt=1 -v fn="testsort_1.csv" '
FNR==1 {
prev=$2
print $0 > fn
}
FNR>1 {
if ($2!=prev) {
cnt++
fn=sprintf("%s_%d.csv", "testsort", cnt)
}
print $0 > fn
prev=$2
}'
注意:您将初始文件名设置为一个变量以开始,然后使用sprintf从cnt计数创建所有后续文件名。prev跟踪上一条记录中的第二个字段。fn是sprintf和计数器创建的文件名
最初将prev声明为变量的同一脚本的较短版本为:
sort -t, -k2 file.csv |
awk -F, -v cnt=0 -v prev="" '{
if ($2!=prev) {
cnt++
fn = "testsort_" cnt ".csv"
prev=$2
}
print $0 > fn
}'
如果您不希望有顺序编号的文件,而是希望从已排序的记录中获取testsort_number.csv,请查看现在已删除的答案,该答案在这方面提供了一个优秀且简短的解决方案。我看你已经有了很好的答案
示例使用/输出
使用file.csv中的输入,将创建以下输出文件:
$ for i in testsort_{1..3}.csv; do printf "\n%s\n" $i; cat $i; done
testsort_1.csv
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A10,PWR-120
WH_01,TRAINAMS,A101,PWR-120
testsort_2.csv
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665
testsort_3.csv
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859
通过为文件名保留一个计数器,并使用sprintf为每个连续的文件组创建文件名,您可以用一种相当简单的方式来实现。您可以使用FNR文件记录编号来区分第一个记录和后续记录 例如:
$ sort -t, -k2 file.csv |
awk -F, -v cnt=1 -v fn="testsort_1.csv" '
FNR==1 {
prev=$2
print $0 > fn
}
FNR>1 {
if ($2!=prev) {
cnt++
fn=sprintf("%s_%d.csv", "testsort", cnt)
}
print $0 > fn
prev=$2
}'
注意:您将初始文件名设置为一个变量以开始,然后使用sprintf从cnt计数创建所有后续文件名。prev跟踪上一条记录中的第二个字段。fn是sprintf和计数器创建的文件名
最初将prev声明为变量的同一脚本的较短版本为:
sort -t, -k2 file.csv |
awk -F, -v cnt=0 -v prev="" '{
if ($2!=prev) {
cnt++
fn = "testsort_" cnt ".csv"
prev=$2
}
print $0 > fn
}'
如果您不希望有顺序编号的文件,而是希望从已排序的记录中获取testsort_number.csv,请查看现在已删除的答案,该答案在这方面提供了一个优秀且简短的解决方案。我看你已经有了很好的答案
示例使用/输出
使用file.csv中的输入,将创建以下输出文件:
$ for i in testsort_{1..3}.csv; do printf "\n%s\n" $i; cat $i; done
testsort_1.csv
WH_01,TRAINAMS,A10,1221-ESD
WH_01,TRAINAMS,A10,PWR-120
WH_01,TRAINAMS,A101,PWR-120
testsort_2.csv
WH_02,TRAINCLE,A1,074-HD-SATA
WH_02,TRAINCLE,A15,102-55665
testsort_3.csv
WH_03,TRAINLON,L10A3,3005-20
WH_03,TRAINLON,L10A3,3005-21
WH_03,TRAINLON,UK-B3,101859
很好的分离排序和awk
!$T中的2-如果在数组T的索引中找不到第二个字段,
{T[$2]=++i}-递增计数器并将第二个字段保存为索引。
{print}-打印每一行
>文件-覆盖、重定向和向文件追加输出
. 我连接字符串和变量
很好的分离排序和awk
!$T中的2-如果在数组T的索引中找不到第二个字段,
{T[$2]=++i}-递增计数器并将第二个字段保存为索引。
{print}-打印每一行
>文件-覆盖、重定向和向文件追加输出
. 我连接字符串和变量
因为你不确定你是否需要排序,这几乎肯定意味着你不需要排序,你只是觉得出于某种原因它会很有用,加上你只是按$2排序,然后根据$2的值分成不同的文件,所以排序毫无用处 实际上,您需要做的是:
awk -F, '{print > ($2".csv")}'
看:
因为你不确定你是否需要排序,这几乎肯定意味着你不需要排序,你只是觉得出于某种原因它会很有用,加上你只是按$2排序,然后根据$2的值分成不同的文件,所以排序毫无用处 实际上,您需要做的是:
awk -F, '{print > ($2".csv")}'
看:
您对grep有何看法?您想指定映射,例如TRAINAMS->testsort_1.cv,还是希望脚本按字母顺序进行计算?请查看awk中的RS变量,以及这个问题中的一些想法:我不介意使用grep。Thanks@Beta,我想使用原始文件名test.csv或testsort.csv,并对结果文件名使用顺序计数器。第2列可以包含任意数量的不同值,而不仅仅是我在示例中使用的3个值。ThanksHow你觉得grep怎么样?你想指定映射,例如TRAINAMS->testsort_1.cv,还是希望脚本按字母顺序进行计算?请查看awk中的RS变量,以及这个问题中的一些想法:我不介意使用grep。Thanks@Beta,我想使用原始文件名test.csv或testsort.csv,并对结果文件名使用顺序计数器。第2列可以包含任意数量的不同值,而不仅仅是我在示例中使用的3个值。谢谢如果文件已排序,则不需要将所有$2值存储在一个数组中,并将其映射到输出文件号$2在T{T[$2]=++i}你只需要2美元=p{++i;p=$2}感谢tail-n+1 testsort*打印文件名和内容的想法,顺便说一句,这比我使用的带有echos和cats的shell循环要好得多!看起来很酷,b
但如果实际输入在第二列中有一百万个不同的值呢?在这种情况下,您将获得过多的打开文件错误。我没有得到在TIf中用输出文件计数散列第二列的要点。如果文件已排序,则不需要将所有$2值存储在一个数组中,并将它们映射到输出文件编号$2在T{T[$2]=++i}你只需要2美元=p{++i;p=$2}感谢tail-n+1 testsort*打印文件名和内容的想法,顺便说一句,这比我使用的带有echos和cats的shell循环要好得多!看起来很酷,但若实际输入在第二列中有一百万个不同的值呢?在这种情况下,您将获得过多的打开文件错误。我没有理解用输出文件计数对第二列进行散列的意思,这几乎是不公平的,但你是对的。如果您将基于字段的记录输出到文件名,也将基于字段的记录输出到文件名,则无需先排序。awk-F,“{split$1,a,;print>testsort_ua[2].csv}”file.csv是否可以避免存储文件名,或者拆分的成本会更高吗?拆分会更慢,因为另一种方法只是在regexp处拆分字符串时进行散列查找,并且当OP希望基于第二个字段中的唯一值生成文件时,会基于第一个字段的唯一值生成文件。我知道在这个例子中,它们似乎是齐头并进的,但是如果idk总是成立的话,那么实际数据中的idk是正确的。感谢Ed,从第一个字段生成文件名的目的仅仅是testsort_1.csv,…@Ed Morton显示的示例输出:in$2.csv是为了可读性,还是有任何其他特定用途?我删除了,它的行为是一样的。谢谢这几乎不公平,但你是对的。如果您将基于字段的记录输出到文件名,也将基于字段的记录输出到文件名,则无需先排序。awk-F,“{split$1,a,;print>testsort_ua[2].csv}”file.csv是否可以避免存储文件名,或者拆分的成本会更高吗?拆分会更慢,因为另一种方法只是在regexp处拆分字符串时进行散列查找,并且当OP希望基于第二个字段中的唯一值生成文件时,会基于第一个字段的唯一值生成文件。我知道在这个例子中,它们似乎是齐头并进的,但是如果idk总是成立的话,那么实际数据中的idk是正确的。感谢Ed,从第一个字段生成文件名的目的仅仅是testsort_1.csv,…@Ed Morton显示的示例输出:in$2.csv是为了可读性,还是有任何其他特定用途?我删除了,它的行为是一样的。谢谢