在一行的最后一个字段上排序

时间:2010-07-11 11:05:39

标签: linux shell sorting

对行列表进行排序的最简单方法是什么,对每行的最后一个字段进行排序?每行可能有不同数量的字段。

这样的东西
sort -k -1

是我想要的,但sort(1)不会使用负数从末尾而不是从开头选择字段。

我也希望能够选择字段分隔符。

编辑:为问题添加一些特异性:我要排序的列表是路径名列表。路径名可以是任意深度,因此可变数量的字段。我想对文件名组件进行排序。

此附加信息可能会更改操纵线条以提取最后一个字段的方式(可能会使用basename(1)),但不会更改排序要求。

e.g。

/a/b/c/10-foo
/a/b/c/20-bar
/a/b/c/50-baz
/a/d/30-bob
/a/e/f/g/h/01-do-this-first
/a/e/f/g/h/99-local

我希望这个列表按文件名排序,所有这些都以数字开头,表示文件的读取顺序。

我在下面添加了我的答案,这就是我目前正在做的事情。我曾希望有一种更简单的方法 - 也许是一种不同的排序实用程序 - 可能无需操纵数据。

11 个答案:

答案 0 :(得分:12)

这是一个Perl命令行(注意你的shell可能要求你逃避$):

perl -e "print sort {(split '/', $a)[-1] <=> (split '/', $b)[-1]} <>"

只需将列表输入其中,或者如果列表位于文件中,则将文件名放在命令行的末尾。

请注意,此脚本实际上并未更改数据,因此您不必注意使用的分隔符。

以下是示例输出:

>perl -e "print sort {(split '/', $a)[-1] <=> (split '/', $b)[-1]} " files.txt
/a/e/f/g/h/01-do-this-first
/a/b/c/10-foo
/a/b/c/20-bar
/a/d/30-bob
/a/b/c/50-baz
/a/e/f/g/h/99-local

答案 1 :(得分:12)

awk '{print $NF,$0}' file | sort | cut -f2- -d' '

基本上,这个命令可以:

  1. 重复开头的最后一个字段,用空格分隔(默认OFS)
  2. 使用完整路径($ 0)排序,解析重复的文件名以进行排序
  3. 剪切重复的第一个字段,f2-表示从第二个字段到最后一个字段

答案 2 :(得分:6)

类似这样的事情

awk '{print $NF"|"$0}' file | sort -t"|" -k1 | awk -F"|" '{print $NF }'

答案 3 :(得分:3)

perl中的单行代码,用于反转行中字段的顺序:

perl -lne 'print join " ", reverse split / /'

您可以使用它一次,将输出通过管道排序,然后将其送回,您就可以实现所需。您可以将/ /更改为/ +/,以便挤压空格​​。你当然可以自由地使用你想要分割线条的正则表达式。

答案 4 :(得分:2)

我认为唯一的解决方案是使用awk

  1. 使用awk将最后一个字段放在前面。
  2. 排序。
  3. 将第一个字段再次放到最后。

答案 5 :(得分:1)

将行上的最后一个分隔符替换为另一个分隔符,该分隔符不会出现在列表中,使用另一个分隔符作为sort(1)分隔符对第二个字段进行排序,然后还原分隔符更改。

delim=/
new_delim=" "
cat $list \
| sed "s|\(.*\)$delim|\1$new_delim|" \
| sort -t"$new_delim" -k 2,2 \
| sed "s|$new_delim|$delim|"

问题是知道列表中没有出现的分隔符。您可以在列表上进行多次传递,然后grep查找一系列潜在的分隔符,但这一切都非常讨厌 - 特别是当“对行的最后一个字段进行排序”的概念如此简单地表达时,但解决方案却没有。

编辑:一个用于$ new_delim的安全分隔符是NUL,因为它不能出现在文件名中,但我不知道如何将NUL字符放入bourne / POSIX shell脚本(不是bash)以及是否排序和sed将妥善处理它。

答案 6 :(得分:0)

#!/usr/bin/ruby

f = ARGF.read
lines = f.lines

broken = lines.map {|l| l.split(/:/) }

sorted = broken.sort {|a, b|
    a[-1] <=> b[-1]
}

fixed = sorted.map {|s| s.join(":") }

puts fixed

如果所有答案都涉及perl或awk,那么也可以用脚本语言解决整个问题。 (顺便说一句,我首先在Perl中尝试过并很快记得我不喜欢Perl的列表列表。我很想看到Perl大师的版本。)

答案 7 :(得分:0)

  

我希望这个列表按文件名排序,所有这些都以数字开头   表示应该读取文件的顺序。

find . | sed 's#.*/##' | sort

sed替换以斜杠结尾的结果列表的所有部分。文件名是什么,你可以对它进行排序。

答案 8 :(得分:0)

这是一个python oneliner版本,请注意它假定该字段是整数,您可以根据需要更改它。

echo file.txt | python3 -c 'import sys; list(map(sys.stdout.write, sorted(sys.stdin, key=lambda x: int(x.rsplit(" ", 1)[-1]))))'

答案 9 :(得分:0)

| sed "s#(.*)/#\1"\\$'\x7F'\# \
| sort -t\\$'\x7F' -k2,2 \
| sed s\#\\$'\x7F'"#/#"

仍然比sort(1)的简单负字段索引更糟糕,但是在这种情况下使用DEL字符作为分隔符应该不会造成任何问题。

我也喜欢它的对称性。

答案 10 :(得分:-1)

sort允许您使用-t选项指定分隔符,如果我记得很清楚的话。要计算最后一个字段,您可以执行类似计算行中分隔符数量和总和的操作。例如像这样的东西(假设“:”分隔符):

d=`head -1 FILE | tr -cd :  | wc -c`
d=`expr $d + 1`

$d现在包含最后一个字段索引。)