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?

  • 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) ? – don_crissti Aug 23 '15 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 '15 at 1:31
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.

  • This worked. Was wondering why the order was reversed, but I see your edit now. – Caleb Xu Aug 23 '15 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
  • 1
    +1 was thinking the same thing: however one should perhaps note that it will have the side effect of de-duplicating the input – steeldriver Aug 23 '15 at 2:30
  • @steeldriver you are absolutely correct, I edited my answer, should be fine now. – jimmij Aug 23 '15 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. – Peter Cordes Aug 23 '15 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 '15 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="")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.