【问题标题】:Bash Sorting a List by ColumnsBash 按列对列表进行排序
【发布时间】:2021-02-07 01:51:55
【问题描述】:

我正在对第 2 列进行反向排序。 至于第 1 列,如果多行具有相同的 $2 值,我希望它们以相反的顺序排序。我目前已将此列表存储在 bash 脚本中的变量中。有sed或者awk函数可以用吗?

例如,我现在的输出是:

123, 3
124, 3
12345, 2
898, 1
1010, 1

我想要的是:

124, 3
123, 3
12345, 2
1010, 1
898, 1

【问题讨论】:

标签: bash list sorting awk sed


【解决方案1】:

结合使用 Perl 单行代码和 sort。单行将, 分隔符转换为制表符(并返回)。 sort 使用 -r 选项进行反向排序,-g 选项用于数字排序。选项-kN,N 指定仅按字段N 排序,这里是第2 个字段,然后是第1 个字段。

perl -pe 's/, /\t/' in_file | sort -k2,2gr -k1,1gr | perl -pe 's/\t/, /' > out_file

例如:

创建示例输入文件:

cat > foo <<EOF
123, 3
124, 3
12345, 2
898, 1
1010, 1
EOF

运行命令:

cat foo | perl -pe 's/, /\t/' | sort -k2,2gr -k1,1gr | perl -pe 's/\t/, /' 

输出:

124, 3
123, 3
12345, 2
1010, 1
898, 1

Perl 单行代码使用这些命令行标志:
-e:告诉 Perl 查找内联代码,而不是在文件中。
-p:循环输入一行一次,默认分配给$_。在每次循环迭代后添加print $_

另请参阅:
perldoc perlrun: how to execute the Perl interpreter: command line switches
perldoc perlrequick: Perl regular expressions quick start

【讨论】:

  • 我能够使用“sort -k2,2gr -k1,1gr”进行管道传输,效果很好!感谢您的意见
【解决方案2】:

这不是一个简单的awk 脚本,但也不难。您只需使用下面的数组a[] 来存储第一个字段的值以获得第二个字段的相等值。如果设置了last(例如,不是第一条记录)并且第二个字段发生更改,则输出当前数组并重置数组(即规则 1)。

在规则 2 中,您只需扫描现有数组并按顺序将当前第一个字段插入数组中。您保留第二个字段的 last 值,以便知道它何时更改。您使用END 规则输出最后一组值,例如

awk -F, '
    last && $2 != last {
        for (i=1; i<=n; i++)
            print a[i]", "last;
        delete a
        n = 0
    }
    {
        swapped=0
        for (i=1; i<=n; i++)
            if ($1 > a[i]) {
                swapped=1
                for (j=n+1; j>i; j--)
                    a[j]=a[j-1]
                a[i]=$1
            }
        if (!swapped)
            a[++n]=$1
        else
            n++
        last=$2
    }
END {
    for (i=1; i<=n; i++)
        print a[i]", "last
    }
' file

swapped 标志只是告诉您当前的第一个字段是在现有元素之前插入到数组中 (swapped == 1) 还是刚刚添加到末尾 (swapped == 0)。

使用/输出示例

在名为file 的文件中使用您的示例文件,您可以简单地切换到包含它的目录,用鼠标选择上面的脚本(将文件名更改为您的文件名),然后middle-mouse - 将脚本粘贴到终端,例如

$ awk -F, '
>     last && $2 != last {
>         for (i=1; i<=n; i++)
>             print a[i]", "last;
>         delete a
>         n = 0
>     }
>     {
>         swapped=0
>         for (i=1; i<=n; i++)
>             if ($1 > a[i]) {
>                 swapped=1
>                 for (j=n+1; j>i; j--)
>                     a[j]=a[j-1]
>                 a[i]=$1
>             }
>         if (!swapped)
>             a[++n]=$1
>         else
>             n++
>         last=$2
>     }
> END {
>     for (i=1; i<=n; i++)
>         print a[i]", "last
>     }
> ' file
124,  3
123,  3
12345,  2
1010,  1
898,  1

检查一下,如果您有任何问题,请告诉我。

【讨论】:

  • 你认为没有数组可以做到这一点吗?
  • 不是一次调用awk。如果您不使用数组,您将产生额外的进程,通过管道将部分输出进行排序等。然后将结果重新组合在一起。单个数组允许按顺序插入第一个字段值,从而有效地提供排序方式。 awk 默认有数组——你为什么不想使用它们?
  • 我想找到多种方法来执行一项任务,我正在试验流水线的多功能性,感谢您的意见!
  • 不用担心——这是一种很好的学习方式。最大的考虑是效率。每个管道和每个其他进程都需要您生成一个额外的子shell,该子shell 重复读取等。如果只有几千行输入 - 那么它在很大程度上是一个清洗,如果有 90,000,000 行,那就是不同的单个呼叫在 60 秒内完成或多个进程花费 10 倍(或更多)时间。尽可能地学习所有不同的方法,并了解这些方法之间的区别——这就是你如何学会在任何工作中使用合适的工具......
  • 如果你在一个循环中生成多个子shell,那么性能会差几个数量级。尽量减少子外壳的数量是关键。
【解决方案3】:

还有awk,你可以试试这个:

awk 'BEGIN{RS=""; OFS=FS="\n"} {tmp2 = $2; $2 = $1; $1 = tmp2; tmp5=$5; $5=$4; $4=tmp5}1' file
124, 3
123, 3
12345, 2
1010, 1
898, 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-08
    相关资源
    最近更新 更多