14

Given input:

hello: world foo bar baz
bar:
baz: bin boop bop fiz bang beep
bap: bim bam bop
boatkeeper: poughkeepsie

I would like to sort it into most words at the top, to least at the end, like so:

baz: bin boop bop fiz bang beep
hello: world foo bar baz
bap: bim bam bop
boatkeeper: poughkeepsie
bar:

How would I do this with sort or some other tool?

2
  • Just to be clear, you want to sort by number of words not sort by line length (with your sample input the line with the most words is also the longest but that might not always be the case) ? Aug 23, 2015 at 1:29
  • Yes. The line with the most words isn't necessarily the longest in general. e.g. I want bin: bop boop before boatkeeper: poughkeepsie. If two lines share the same number of words, I would prefer the ties to be alphabetized, but that's not a requirement.
    – Caleb Xu
    Aug 23, 2015 at 1:31

4 Answers 4

22

You could do something like:

awk '{print NF,$0}' file | sort -nr | cut -d' ' -f 2-

We use awk to prefix the number of fields to each line. We then sort by that number and remove it with cut.

1
  • This worked. Was wondering why the order was reversed, but I see your edit now.
    – Caleb Xu
    Aug 23, 2015 at 1:48
6

In recent GNU awk one can use PROCINFO array to define many internal parameters including order in which array elements are printed (controlled by element "sorted_in"). Thus we can built and array indexed with the value of NF" "NR, which elements have value of $0 and print it in desired output, in your case that would be "@ind_num_desc":

awk '{a[NF" "NR]=$0}END{PROCINFO["sorted_in"]="@ind_num_desc"; for(i in a) print a[i]}' file
4
  • 1
    +1 was thinking the same thing: however one should perhaps note that it will have the side effect of de-duplicating the input Aug 23, 2015 at 2:30
  • @steeldriver you are absolutely correct, I edited my answer, should be fine now.
    – jimmij
    Aug 23, 2015 at 2:40
  • This now preserves the original ordering between records with the same number of fields, instead of sorting on the words as a secondary sort key. If your keys were NF" "$0" "NR, you'd only have NR as a fallback / duplicate-handling mechanism. Aug 23, 2015 at 2:44
  • 1
    @PeterCordes but that would reverse the order of words, I see no way to resolve ties alphabetically other that by definition own function cmp_func() - gnu awk allows that.
    – jimmij
    Aug 23, 2015 at 3:12
5

Perl one-liner:

print sort { split(' ',$a) <=> split(' ',$b) } <>;

If you want to break ties using alphabetical order:

print sort { split(' ',$a) <=> split(' ',$b) or $a cmp $b } <>;
4

Through python.

s = '''hello: world foo bar baz
bar:
baz: bin boop bop fiz bang beep
bap: bim bam bop'''.splitlines()
for i in sorted(s, key=lambda x: len(x.split()), reverse=True):
    print(i)

or

with open('/path/to/the/input/file') as f:
    m = f.readlines()
    for i in sorted(m, key=lambda x: len(x.split()), reverse=True):
        print(i, end="")

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .