使用shell对行的条目进行排序

时间:2013-05-16 00:40:41

标签: linux bash shell unix sorting

考虑以下输入和输出:

  infile   |   outfile
1 3 5 2 4  |  1 2 3 4 5
2 4 5      |  2 4 5
4 6 2 1    |  1 2 4 6

是否存在任何组合的UNIX程序,不涉及编程语言 - 除了shell脚本本身之外的其他任何东西 - 对每行中的条目进行排序文件比以下方法更快:

while read line; do
    tr ' ' '\n' <<< ${line} | sort | tr '\n' ' '
    echo ""
done < infile > outfile

我的意思是,我可以创建一个小cpp/python/awk/...程序,但这与使用通常的 one-liners 来神奇地解决问题是不一样的。

修改

我必须添加太多文字,而不是简单地问我想要什么;直截了当地,我想确认是否有任何UNIX程序/ 程序组合(使用管道,fors,whiles,...)能够对一行中的条目进行排序,但没有像上面有一个解决方案。

我知道我可以用编程语言来完成讨厌的工作,比如perl,awk,python,但我实际上是在寻找一个不涉及这些语言解释器的UNIX程序组合。从答案中,我必须得出结论,没有这样的inline sort工具,我非常感谢我得到的解决方案 - 主要是非常简洁的Perl单线程。

然而,我并不真正理解我发布的Bash方法上有如此多开销的原因。这真的是由于大量的上下文切换,还是仅仅是为了对输入进行翻译和排序的开销?

我似乎无法理解这些步骤中的哪一步会减慢执行速度。使用~500k行对文件中的条目进行排序需要几分钟,每行约30个值。

4 个答案:

答案 0 :(得分:2)

Perl可以很好地完成单行Unix / Linux命令:

perl -n -e "print join ' ', sort{a<=>b} split ' '" < input.txt > output.txt

这是“古老的”Perl,在ab之前没有美元,它允许命令在Windows和bash shell中正常运行。如果您将美元与bash一起使用,则必须使用反斜杠进行转义,或者必须反转单引号和双引号。

请注意,您尝试在命令,编程语言和程序之间绘制的区别非常薄。 Bash是一种编程语言。 Perl当然可以用作shell。两者都是命令。

脚本运行缓慢的原因是它每次循环迭代产生3个进程。流程创建非常昂贵。

答案 1 :(得分:1)

#!awk -f
{
  baz = 0
  PROCINFO["sorted_in"] = "@val_num_asc"
  split($0, foo)
  for (bar in foo)
    $++baz = foo[bar]
}
1

结果

1 2 3 4 5
2 4 5
1 2 4 6

答案 2 :(得分:1)

问题比看起来更微妙。您似乎在询问是否有更快的方式来执行排序,并且您通过Perl和awk等获得了很多(优雅!)答案。但你的问题似乎是你是否可以使用shell 内置插件进行更快的排序,为此,答案是否定的。

显然,sort不是内置的shell,也不是tr。没有内置功能可以做什么,并且可能替代“tr”的内置函数不太可能在这里帮助你(比如说要操作bash的IFS变量需要花费太多工作来删除对tr的调用只是与tr)一起生活。

就个人而言,我会选择Perl。请注意,如果您的数据集很大或很时髦,您可以选择使用sort pragma更改Perls默认排序算法。我不认为你需要它来整理整数文件,但也许这只是你的一个例子。

答案 3 :(得分:0)

它不漂亮(绝对不是1-liner),但你可以只使用内置shell命令对行进行排序,但是对于短行,它可能比重复调用外部函数更快。

#!/bin/sh
sortline(){
for x in $@;do
    [ ! "$FIRST" ] && FIRST=t && set --
    i=0
    while [ $i -le $# ];do
        [ $x -lt $((${@:$((i+1)):1})) ] && break || i=$((i+1))
    done
    set -- ${@:1:$i}  $x   ${@:$((i+1)):$(($#-$i))}
done
echo $@
}
while read LINE || [ "$LINE" ];do
    sortline $LINE
done <$1 >$2

编辑:顺便说一下这是一个选择排序算法,以防有人想知道

Edit2:这仅用于数值,对于你需要使用某些比较的字符串,如[ "$x" -lt "${@:$((i+1)):1}" ](未选中),但是我将这个C程序用于字符串(我只称它为qsort),但它可以在argv上使用atoi进行修改:

#include <stdlib.h>
#include <string.h>
static inline int cmp(const void *a, const void *b){
   return strcmp(*(const char **)a, *(const char **)b);
}

int main(int argc, char *argv[]){
    qsort(++argv, --argc, sizeof(char *), cmp);
    while (argc){
      write(1,argv[0],strlen(argv[0]));
      write(1,(--argc && argv++)?"\t":"\n",1);
   }
}