Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I think I'm pretty experienced now in sorting by columns; however, I haven't found anything so far how to sort continuous rows.

Supposing we have a text file that looks like this: (very simplified, of course)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Now, is it possible to sort the lines alphanumerically per each block separately? I mean, so that the result looks like this:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Telling from what I found in the sort man page, this might not be possible with the built-in UNIX sortcommand. Or can it even be done without having to resort to external/third-party tools?

share|improve this question

3 Answers 3

up vote 7 down vote accepted

Drav's awk solution is good, but that means running one sort command per paragraph. To avoid that, you could do:

< file awk '!NF{n++};{print +n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Or you could do the whole thing in perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Note that above, separators are blank lines (for the awk one, lines with only space or tab characters, for the perl one, any horizontal or vertical spacing character) instead of empty lines. If you do want empty lines, you can replace !NF with !length or $0=="", and /\S/ with /./.

share|improve this answer
    
Thanks to you too, especially for the awk solution which avoids the sort overhead! Sneaky! –  syntaxerror Aug 14 '13 at 21:46
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Setting the record separator RS to an empty string makes awk step in paragraphs at a time. For each paragraph, pipe the paragraph (in $0) to cmd (which is set to sort) and print the output. Print out a blank line to separate the output paragraphs with a print "".

If we're giving perl examples, then I present an alternative approach than that of Stephane's:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Unset the field separator (undef $/), this allows us to use <> and get the whole of STDIN. We then split that around \n\n (paragraphs). foreach "paragraph", sort the lines by splitting around newlines, sorting and then joining them back together and tacking on a trailing \n.

However, this has one side effect of adding a "trailing paragraph" separator on the last paragraph (if it didn't have one before). You can get around that with the slightly less pretty:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

This assigns the paragraphs to @list, and then there is a "ternary operation" to check if it is the last element of the foreach (the \$_ == \$list[-1] check). print "" if it is (? ...), else (: ...) print "\n" for all other "paragraphs" (elements of @list).

share|improve this answer
    
This is neat! Thank you. Are you actually invoking /usr/bin/sort with that line or is it an awk built-in "sort" command? –  syntaxerror Aug 14 '13 at 18:41
    
Invoking the sort command, hence the requirement to close(cmd) on each loop :) –  Drav Sloan Aug 14 '13 at 18:44

If you have GNU awk available you could sort each block using the built-in asort() function. Something like this:

blocksort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Run it like this:

awk -f blocksort.awk infile
share|improve this answer
    
Another good approach! Thanks! –  syntaxerror Aug 15 '13 at 22:51

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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