如何在Linux命令行中按数字顺序对文件进行排序

17

这不仅是关于Linux的发泄,也是一个问题,但也许有人知道如何做到我想要的。我知道使用sort命令可以实现,但我希望有更好的方法,因为让它工作起来就像编写C程序那样困难。

举个例子,我有以下文件:

  • file-10.xml
  • file-20.xml
  • file-100.xml
  • file-k10.xml
  • file-k20.xml
  • file-k100.xml
  • file-M10.xml
  • file-M20.xml
  • file-M100.xml

我希望它们按照以下顺序排序,这正是Windows默认排序的方式。 Windows将连续的数字字符组合成一个有效字符,然后按字母表顺序排列。

在Linux命令行中输入ls命令,会得到下面的结果。注意,数字20被错位了。当我想按顺序查看数百个这样的文件时,这是一个更大的问题。

我可以使用ls -1 | sort -n -k 1.6来获取没有'k'或'M'的正确顺序...

如果我使用ls -1 | sort -n -k 1.7,则完全不正确。

让我们真正地把它弄对。 ls -1 | grep "file-[0-9]*\.xml" | sort -n -k1.6 && ls -1 file-k*.xml | sort -n -k1.7 && ls -1 file-M*.xml | sort -n -k1.7

Windows的行为简单、优雅,并且在99%的情况下都能做到你想做的事情。为什么Linux不能这样做?为什么sort没有一个“自动排序数字而不让我撞墙”的开关呢?

以下是C++的伪代码:

bool compare_two_strings_to_avoid_head_injury(string a, string b)
{
    string::iterator ai = a.begin();
    string::iterator bi = b.begin();
    for(; ai != a.end() && bi != b.end(); ai++, bi++)
    {
        if (*ai is numerical)
            gobble up the number incrementing ai past numerical chars;
        if (*bi is numerical)
            gobble up the number incrementing bi past numerical chars;
        actually compare *ai and *bi and/or the gobbled up number(s) here
            to determine if we need to compare more chars or can return the 
            answer now;
    }
    return something here;
}

那有那么难吗?有人能将这个排序并给我一份副本吗?求求了?


8
如果在数字字段前添加前导零以使它们的长度相同,而不是依赖特定于平台的怪异性来获得所需的排序顺序,那么你本可以减少一些痛苦。只是说一下…… - Jim Lewis
2
我想指出的是,也许Windows的行为在99%的情况下符合您的期望,但不能说它在99%的情况下都符合每个人的期望。事实上,我完全可以就Windows的排序问题提出与您关于Linux排序问题相同的抱怨。(不过将其作为选项添加到“sort”中会很好) - David Z
3
这里的编程问题是什么?如果你只想对文件名进行排序,可以去http://superuser.com寻求帮助。 - Gabe
Windows 并非总是以这种方式进行排序。请参见 http://support.microsoft.com/kb/319827。 - fpmurphy
2
@Scott:是的,你确实使用了一个特定于平台的技巧,即dir将连续的数字分组为“有效字符”,而ls则不会。尽管从技术上讲,这是dir程序的怪癖,而不是Windows的怪癖。同样,你所谓的Linux问题实际上是一个特定程序sort的“问题”。(此外,它并不像合法的错误那样成为问题,它只是一个设计决策,恰好不符合你的要求。这在每个平台上都会时有发生。) - David Z
显示剩余2条评论
3个回答

37

尝试运行命令:sort --version-sort -f

  • file-10.xml
  • file-20.xml
  • file-100.xml
  • file-k10.xml
  • file-k20.xml
  • file-k100.xml
  • file-M10.xml
  • file-M20.xml
  • file-M100.xml

-f选项用于忽略大小写(否则,在本例中,它会按错误的顺序放置k和M)。但是,如果您的目标是将字母k和M解释为千位和百万位,我认为sort并未正确地对其进行解释 - 它只是按字母顺序排序。


5
比被选择的答案更好的解决方案...可能不太便携,我猜。 -V--version-sort 的短标志,供参考。 - Tony Cesaro

16

ls -1v可以接近实现您的目标。它只是按照字母顺序排序,将大写字母排在小写字母前面。


这也适用于具有不同位数的数字:1 2 3 ... 12 13 14 ... 123 124 125 ... 1123 1124 1125 ... - Amir

2
这是我的第一个想法:
ls -1 | sed 's/\-\([kM]\)\?\([0-9]\{2\}\)\./-\10\2./' | sort | sed 's/0\([0-9]\{2\}\)/\1/'

基本上,我只是使用 sed 在数字前面加上零,并在之后再次使用它来去掉前导零。

我不知道在Perl中是否更快。


1
这是我根据您的建议最终采取的做法。由于我需要多达4位数字,因此我使用了以下代码:for f in \ls -1 $1*.xml | sed -r 's/-([kM]?)([0-9]{4})./-\10\2./; s/-([kM]?)([0-9]{3})./-\100\2./; s/-([kM]?)([0-9]{2})./-\1000\2./; s/-([kM]?)([0-9]{1})./-\10000\2./' | sort | sed -r 's/0+([1-9])/\1/'`; do 我觉得这对于如此简单的任务来说非常荒谬。在我看来,这是sort`的一个很大的缺陷。 - Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接