【问题标题】:How to sort a file based on key name instead of its position in unix?如何根据键名而不是它在 unix 中的位置对文件进行排序?
【发布时间】:2022-01-12 11:03:28
【问题描述】:

我想在 Unix 中对文件进行排序,为此我正在使用命令

sort file --field-separator=' ' --key=7,7

但是这个字段的位置不是固定的,有时可能是第7个字段,有时也可以是第6个或第8个字段。

我们是否知道是否可以根据字段名称对文件进行排序,例如

sort file --field-separator=' ' --keyname=<my_unique_id>

文件长这样,我想按照party_id排序

status_date="2000-01-31" ref_date="2021-03-31" ead_percent="0.00365316" accounting_standard="IFRS" party_default_status_cd="NOTDFLT" party_id="36113477" v_src_system_id="ABC"
status_date="2002-12-31" ref_date="2021-03-31" ead_percent="1" accounting_standard="IFRS" orig_src_system_id="GRD" party_default_status_cd="UNLIKE" party_id="36053415" v_src_system_id="XYZ"

【问题讨论】:

  • 列由索引组成,而不是键,并且您没有显示输入文件的样子。

标签: bash sorting unix awk


【解决方案1】:

sort 没有命名键的概念,但您可以执行 Schwartzian transform 临时将键作为前缀添加到该行,对第一个字段进行排序,然后丢弃它。

sed 's/\(.*\)\(party_id="[^"]*"\)/\2    \1\2/' file |
sort -t '   ' -k1,1 |
cut -f2-

(其中两个第一个反向引用之间的空格和sort -t 参数中的空格是文字制表符,但 Stack Overflow 将其呈现为一系列空格)。

【讨论】:

  • 您根本不必使用标签;例如,使用逗号或冒号等常见标点字符会更容易一些事情,但是分隔符与数据中的其他内容发生冲突的风险更大。
  • 感谢三人组。有用。你能解释一下第1行和第3行在做什么吗?另外,如果我需要为多级排序添加另一个键,我需要在您的命令中进行哪些更改?
  • 对,我只是说你所拥有的不是那个,它是一个普通的旧装饰/排序/取消装饰,因为它不是那个成语的 perl 实现。手指越过这个术语在其他应用程序中并没有真正流行起来,或者它就像 Joe Smith 用 perl 或任何其他工具/语言编写二进制排序并突然二进制排序被称为 Smiths 变换一样!跨度>
  • 呃。好吧,再过 10 年,无论如何,所有单词都会被表情符号取代,然后就不再重要了:-)。
【解决方案2】:

使用 Decorate/Sort/Undecorate 习语并假设,就像您提供的示例一样,您引用的字符串不包含空格、="

装饰:

$ awk -F'[ ="]+' -v OFS='\t' -v keyname='party_id' '{for (i=1; i<NF; i+=2) if ($i == keyname) { print $(i+1), $0; next} }' file
36113477        status_date="2000-01-31" ref_date="2021-03-31" ead_percent="0.00365316" accounting_standard="IFRS" party_default_status_cd="NOTDFLT" party_id="36113477" v_src_system_id="ABC"
36053415        status_date="2002-12-31" ref_date="2021-03-31" ead_percent="1" accounting_standard="IFRS" orig_src_system_id="GRD" party_default_status_cd="UNLIKE" party_id="36053415" v_src_system_id="XYZ"

装饰然后排序:

$ awk -F'[ ="]+' -v OFS='\t' -v keyname='party_id' '{for (i=1; i<NF; i+=2) if ($i == keyname) { print $(i+1), $0; next} }' file |
    sort -k1,1n
36053415        status_date="2002-12-31" ref_date="2021-03-31" ead_percent="1" accounting_standard="IFRS" orig_src_system_id="GRD" party_default_status_cd="UNLIKE" party_id="36053415" v_src_system_id="XYZ"
36113477        status_date="2000-01-31" ref_date="2021-03-31" ead_percent="0.00365316" accounting_standard="IFRS" party_default_status_cd="NOTDFLT" party_id="36113477" v_src_system_id="ABC"

装饰然后排序然后取消装饰:

$ awk -F'[ ="]+' -v OFS='\t' -v keyname='party_id' '{for (i=1; i<NF; i+=2) if ($i == keyname) { print $(i+1), $0; next} }' file |
    sort -k1,1n |
    cut -f2-
status_date="2002-12-31" ref_date="2021-03-31" ead_percent="1" accounting_standard="IFRS" orig_src_system_id="GRD" party_default_status_cd="UNLIKE" party_id="36053415" v_src_system_id="XYZ"
status_date="2000-01-31" ref_date="2021-03-31" ead_percent="0.00365316" accounting_standard="IFRS" party_default_status_cd="NOTDFLT" party_id="36113477" v_src_system_id="ABC"

【讨论】:

  • 也许使用match 筛选出装饰比遍历字段更简单。
  • @FelixJN 健壮和可扩展地使用 match() 有点困难,因为默认情况下您正在处理部分正则表达式匹配而不是全字字符串匹配,因此您必须考虑正则表达式元字符和子字符串如果您想扩展逻辑以处理多个目标,那么如果/当目标以不同的顺序出现时,添加到正则表达式的明显选择会失败。这没什么大不了的,但恕我直言,带有全字字符串比较的循环更加清晰和简单。
【解决方案3】:

使用GNU awk (gawk) 可以指定如何遍历数组。以下使用party_id=XYZ 作为各自的索引将每一行保存在数组中,然后返回按所述索引排序的数组。非常大的文件受 RAM 限制。

awk '{match($0,/party_id=[^ ]*/,$0,id) ; arr[id[0]]=$0}
     END {PROCINFO["sorted_in"]="@ind_str_asc"
          for (i in arr) {print arr[i]}
     }' infile.txt 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-18
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 2020-04-01
    相关资源
    最近更新 更多