【问题标题】:bash sort quoted csv files by numeric keybash按数字键对引用的csv文件进行排序
【发布时间】:2014-09-01 12:53:23
【问题描述】:

我有以下输入 csv 文件:

"aaa","1","xxx" "ccc, Inc.","6100","yyy" "bbb","609","zzz"

我希望按第二列作为数字排序, 我试过了

排序 --field-separator=',' --key=2n

问题在于,由于所有值都被引用,它们没有被 -n(数字)选项正确排序。有解决办法吗?

【问题讨论】:

    标签: bash sorting csv


    【解决方案1】:

    一个小技巧,使用双引号作为分隔符:

    sort --field-separator='"' --key=4 -n
    

    【讨论】:

    • 对于其他读者:注意 key=4,而不是 key=2 ;-)
    【解决方案2】:

    对于引用的csv,请使用具有适当csv 解析器的语言。这是使用perl 的示例。

    perl -MText::ParseWords -lne '
        chomp; 
        push @line, [ parse_line(",", 0, $_) ];
    }{ 
        @line = sort { $a->[1] <=> $b->[1] } @line;
        for (@line) {
            local $" = qw(",");
            print qq("@$_");
        }
    ' file
    

    输出:

    "aaa","1","xxx"
    "bbb","609","zzz"
    "ccc, Inc.","6100","yyy"
    

    说明:

    • 使用chomp 函数从输入中删除新行。
    • 使用代码模块Text::Parsewords 解析带引号的行并将其存储在不带引号的数组数组中。
    • END 块中,对第二列的数组进行排序并将其分配给原始数组。
    • 对于我们数组中的每个项目,我们将输出列表分隔符设置为",",并使用前面和后面的" 打印它以创建原始格式的行。

    【讨论】:

      【解决方案3】:

      将您的示例放入名为 sort2.txt 的文件中,我发现以下内容运行良好。
      sort -t'"' -k4n sort2.txt 使用以下命令排序(感谢乔纳森的改进)

    • -t[可选的单字符分隔符,而不是制表符。定义在单引号内]'"'。
    • -k4 选择第四个key中的值。(k)用"分隔,第4个key的值
    • -n 数字排序
    • 文件名避免使用不必要的链接
    • 希望这会有所帮助!

      【讨论】:

      • less 的行为类似于cat,当它的输出进入管道时,sort 完全能够读取文件(所以sort -t '"' -k 4n sort2.txt 会更好;它也会按数字排序)。
      • 此外,包含"Joe ""The Man"" Bloggs"(有效 CSV)的第一个字段会使您的字段计数非常糟糕。你也可以在我的回答中提出这个抱怨,但它需要像 "Joe ""The Man"",""The Guy"" Bloggs" 这样的东西来混淆它,这更加深奥(我的一个“合理假设”是这样的字符串不会出现 - 有是嵌入逗号后的空格)。
      • 根据您的建议更新,乔纳森。谢谢,我忘了sort可以直接拉文件。
      【解决方案4】:

      不会有一个非常简单的解决方案。如果你做出一些合理的假设,那么你可以考虑:

      sed 's/","/^A/g' input.csv |
      sort -t'^A' -k 2n |
      sed 's/^A/","/g'
      

      这会将"," 序列替换为Control-A(在代码中显示为^A),然后将其用作sort 中的字段分隔符(第2 列的数字排序),然后再次将 Control-A 字符替换为 ","

      如果你使用bash,你可以使用ANSI C quoting机制$'\1'将控制字符可见地嵌入到脚本中;你只需要在转义之前完成单引号字符串,然后重新启动它:

      sed 's/","/'$'\1''/g' input.csv |
      sort -t$'\1' -k 2n |
      sed 's/'$'\1''/","/g'
      

      或者使用双引号而不是单引号,但由于您要替换双引号,这会变得混乱。但是您可以简单地逐字输入字符,vim 等编辑将很乐意向您展示。

      【讨论】:

      • @DaleAnderson — 谢谢!修复了,我相信。
      • 太棒了。在 bash 命令中,排序应该仍然使用^A,还是应该使用$'\1'
      • Bash ANSI-C Quoting 运算符 $'\1' 仅使用可打印字符(反斜杠和数字 1),而第一个命令序列中的 ^A 必须输入为 control-A。它们映射到同一个字符。您可以使用其中任何一个;您可以在其中一个中使用^A,在另一个中使用$'\1',但这可能会不必要地混淆人们。 (是的,我的代码自相矛盾;很抱歉,再次感谢 Dale Anderson!)
      【解决方案5】:

      有时 CSV 文件中的值是可选的,仅在必要时才被引用。在这种情况下,使用" 作为分隔符是不可靠的。

      例子:

      "Forest fruits",198
      Apples,456
      bananas,67
      

      使用awksortcut,您可以对原始文件进行排序,这里按第一列:

      awk -F',' '{
          a = $1; # or the column index you want
          gsub(/(^"|"$)/, "", a);
          print a","$0
      }' file.csv | sort -k1 | cut -d',' -f1 --complement
      

      这会将您要排序的列放在前面,不带引号,然后按照您想要的方式对其进行排序,并在最后删除此列。

      【讨论】:

        猜你喜欢
        • 2012-08-09
        • 2015-11-13
        • 2018-12-17
        • 2015-12-24
        • 2017-10-08
        • 2010-12-17
        • 2012-11-01
        • 2015-06-13
        • 1970-01-01
        相关资源
        最近更新 更多