I have a large text file, where each line consists of three numbers, except for a few comment lines that all start with a hashtag (#). I want to verify that the non-comment lines are sorted numerically. Is there a good way to do this?

I assume I have to extract all the non-comment lines with something like grep -ve \# - butwhere do I pipe this to verify that the output is sorted?

Note: I am not looking for a way to sort the file, but rather to verify that it already is sorted (to verify output the output of a program of mine). Unfortunately, this isn't as common as wanting to sort the contents of a file, or (even more so) wanting to list the contents of a folder in a particular order (alphabetically or by size, for example), so Google has very little for me...

Clarification: The numbers in the file are real, usually on exponential form. I want them sorted in numerical order, which for example means that 0.11000E+02 > 0.90000E+01 > 0.15000E-01.

If it simplifies any, I know that if the file matches what I want, the first two columns will be sorted if counted in pairs - in other words, if the file is valid the third column doesn't have to be considered at all.

Formally, you can express it like this: if x1 and y1 are the first two numbers on one line, and x2 and y2 are the first two numbers on another line, then (x1,y1)>(x2,y2) iff (x1>x2) || (x1==x2 && y1>y2). (x1,y1)>(x2,y2) here means that the line with x1 and y1 should be considered larger, the line with x2 and y2, and (x1,y1) should appear below (x2,y2) in the file.

Sample input: pastebin
I expect the above file to be considered as sorted, but if any two lines (that are not comment lines) are switched, the file is no longer sorted. Note that lines can have leading whitespace.

#!/usr/bin/perl -w
use strict;

unless ( @ARGV == 1 && -f -r $ARGV[0] ) {
    die "Expected single file argument!\n";

my %cols;
my $ind = 0;

while (<>) {
    next if /^\s*($|#)/;
    ( @{ $cols{col1} }[$ind], @{ $cols{col2} }[$ind], @{ $cols{col3} }[$ind] ) = split;

my @sorted1 = map { ${ $cols{col1} }[$_] } sort {
    ${ $cols{col1} }[$a] <=> ${ $cols{col1} }[$b] or
    ${ $cols{col2} }[$a] <=> ${ $cols{col2} }[$b] or
    ${ $cols{col3} }[$a] <=> ${ $cols{col3} }[$b]
} keys @{ $cols{col1} };
my @sorted2 = map { ${ $cols{col2} }[$_] } sort {
    ${ $cols{col1} }[$a] <=> ${ $cols{col1} }[$b] or
    ${ $cols{col2} }[$a] <=> ${ $cols{col2} }[$b] or
    ${ $cols{col3} }[$a] <=> ${ $cols{col3} }[$b]
} keys @{ $cols{col2} };

if ( "@sorted1" eq "@{ $cols{col1} }" and "@sorted2" eq "@{ $cols{col2} }") {
    print "File is sorted!\n"
else { print "File is unsorted!\n" };

If the columns are:

X1 Y1 Z1  
X2 Y2 Z2

Sort will be:

if (x1 > x2) then X1 Y1 Z1 > X2 Y2 Z2
if (X1 == X2) && (Y1 > Y2) then X1 Y1 Z1 > X2 Y2 Z2

To add more columns into the sort order, copy the pattern for the first two. I hope that's what you asked for.


I recently had this question and I used sort --c from bash. This will only check the presence of the first unsorted element and report it. It can be combined with other flags to decide the type of sorting to be checked (e.g numeric or alphabetic)

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.