Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 使用sed或awk对CSV文件进行排序和拆分_Bash_Awk_Sed - Fatal编程技术网

Bash 使用sed或awk对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

我有一个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,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是为了可读性,还是有任何其他特定用途?我删除了,它的行为是一样的。谢谢