【问题标题】:Sorting an array, but possible duplicates对数组进行排序,但可能重复
【发布时间】:2014-05-07 02:33:42
【问题描述】:

我有以下 bash 脚本,它从文件中提取数字列表。我想维护他们被拉动的顺序的日志(这是重要信息)。所以我得到了一些帮助(可能来自我在这里找到的一个例子)将信息转储到一个数组中,排序和输出信息。

if [ ! -z "$sort" ]; then
  if [[ $sort == ascending ]]; then
    gawk '/SCF Done/\
           {c++; list[$5]=c}
           END {
                 asorti(list,energies);
                 for (i=1;i<=c;i++)
                 printf("%s%s%d\n",energies[i]," - Optimization Step #",list[energies[i]])
                 print "Total Optimization Steps: "c}
           ' "$1"

唯一的问题是,我发现存储在行中$5 字段中的值有可能重复。所以在数组的初始构建过程中,list[$5],这个值可能是非唯一的,因此之前的c 值会被覆盖。我已经想到了一些事情(将$5 的值乘以某个随机数,然后再将其重新划分),但如果有一个已经建立(并且更有效)的处理方法,我不会感到惊讶这个我不知道的问题。

这是grep "SCF Done"的输出

 SCF Done:  E(UM11L) =  -1267.67892101     A.U. after   41 cycles
 SCF Done:  E(UM11L) =  -1267.64771239     A.U. after   43 cycles
 SCF Done:  E(UM11L) =  -1267.67892101     A.U. after   39 cycles
 SCF Done:  E(UM11L) =  -1267.67892578     A.U. after   24 cycles
 SCF Done:  E(UM11L) =  -1267.67892051     A.U. after   24 cycles
 SCF Done:  E(UM11L) =  -1267.67892201     A.U. after   22 cycles

我切换到 gawk 格式的全部原因是因为我想提取那些中间数字,然后还创建一个格式化输出,如下所示。我最初使用了一个简单的grep "SCF Done" 语句,但后来获取格式、排序等,开始变成一个相当麻烦的语句来编写。事实还是一样,我希望能够按这些数字排序,同时保留数字和优化步骤之间的相关性(如下所示)。但这些数字并不总是唯一的。

-1267.67892101 - Optimization Step #1
-1267.64771239 - Optimization Step #2
-1267.67892101 - Optimization Step #3
-1267.67892578 - Optimization Step #4
-1267.67892051 - Optimization Step #5
-1267.67892201 - Optimization Step #6

【问题讨论】:

    标签: arrays bash sorting gawk


    【解决方案1】:

    你为什么用gawk而不是sort排序?

    我不太明白你想从你的代码 sn-p 中完成什么,但也许:

    grep 'SCF Done' "$1" | cut -f5 | cat -n | sort -k 2
    

    我明白了。调用排序而不是使用 awk 的数组排序怎么样。

    awk '
        /SCF Done/ {
            printf "%s - Optimization step #%d\n", $5, ++n | "sort"
        } 
        END {
            close("sort")
            print "total steps:", n
        }
    ' file
    

    看起来像:

    -1267.64771239 - Optimization step #2
    -1267.67892051 - Optimization step #5
    -1267.67892101 - Optimization step #1
    -1267.67892101 - Optimization step #3
    -1267.67892201 - Optimization step #6
    -1267.67892578 - Optimization step #4
    total steps: 6
    

    【讨论】:

    • 我希望能够使用我在 gawk 的 printf 语句中显示的其他详细信息来格式化输入。我将使用 grep "SCF Done" 行的输出来修改帖子。
    • 好的,但是 awk 的数据结构妨碍了你。您可能可以使用我的管道并通过 sed(或 awk)运行它以产生您想要的输出。
    • grep | cut | cat -n ?当你开始这样做时,你应该简单地使用 awk 并一次完成所有步骤:awk /SCF Done/ '{print $5}'
    • 您修改后的解决方案非常完美。正是我想要的。您能否添加一些关于“排序”和关闭(“排序”)的管道的解释。我也很容易(谢天谢地)将它更改为 sort -r 以获得我想要包含的反向选项。
    • 请问,是否可以对其进行修改,以便在“SCF Done”行上方恰好 2 行时显示“未满足收敛标准”,以跳过该输出?
    【解决方案2】:

    我是否错过了排序发挥作用的地方?如果您担心重复行,如果它与您的上一行相同,只需跳过该行:

    $ awk 
        'END { print "total steps: " count }
         /SCF Done/ {
            if ( prev5 == $5 ) {
                 continue  # Skip duplicate line
            }
            count++
            printf "%s - Optimization step #%d\n", $5, count
            prev5 = $5
        }'
    

    如果您真的不希望一行重复,请使用数组来存储 $5 的值作为数组的键。然后,您可以使用该数组来查看您是否曾经打过那条线。 awk 中的所有数组都是真正的哈希:

    $ awk 
        'END { print "total steps: " count }
         {
            if ( $0 ~ /SCF Done/  ) {
                if ( prev[$5] == 1 ) {
                    continue  # Seen that value of $5 before. Skip
                }
                count++
                printf "%s - Optimization step #%d\n", $5, count
                prev[$5] = 1  # Mark that you've printed $5 out
            }
        }'
    

    【讨论】:

    • 我接受了另一个答案,但正如我所指出的,我需要保持拉取值的顺序(即使它们是重复的),因为有时这些值会重复但在不同的点。我需要保留“SCF Done”的每个实例,而不是丢弃重复项。
    猜你喜欢
    • 1970-01-01
    • 2021-07-25
    • 2021-07-25
    • 1970-01-01
    • 2017-05-21
    • 2020-05-17
    • 2021-11-04
    • 1970-01-01
    • 2018-04-20
    相关资源
    最近更新 更多