Coreutils 5.93 is really old. The newer versions have a nice --debug
option that shows you how your field selectors apply to each line.
The problem you're having is that sort field-splitting by default includes the whitespace between fields as part of the following field. So in your case, the second field is " (1)"
with a leading space so -k2.2
is selecting a substring starting with a parenthesis, which then fails to be recognized as a number.
You can fix this by adding the b
flag. sort -k2.2nb
or sort -k2.2 -n -b
should work.
On a more recent coreutils (8.23), I can't reproduce your behavior of sort -k2.2 -n
- it behaves the same as sort -k2.2n
, failing to match the numbers and falling back on the "whole line string comparison" sort.
UPDATE (3): Now I have reproduced your result, in versions 5.93, 8.13, and 8.23. And there is a definite explanation for it.
Both of my suggestions (sort -k2.2nb
and sort -k2.2 -n -b
) work. Your sort -b -k2.2n
doesn't. In a version that supports --debug
, it says:
$ printf '%s\n' 'x ('{1,2,10,11}')'|sort -k 2.2n -b --debug
sort: using simple byte comparison
sort: leading blanks are significant in key 1; consider also specifying 'b'
sort: key 1 is numeric and spans multiple fields
sort: option '-b' is ignored
x (1)
^ no match for key
_____
x (10)
^ no match for key
______
x (11)
^ no match for key
______
x (2)
^ no match for key
_____
$
The option '-b' is ignored
is the explanation of what's happening. As soon as you attach one flag (the n
) to the a key specification given with -k
, all globally-specified flags are ignored for that key.
The GNU info documentation for coreutils makes this point clearly (I marked the important part in bold):
The following options affect the ordering of output lines. They may be specified globally or as part of a specific key field. If no key fields are specified, global options apply to comparison of entire lines; otherwise the global options are inherited by key fields that do not specify any special options of their own. In pre-POSIX versions of ‘sort’, global options affect only later key fields, so portable shell scripts should specify global options first.
(and -b
is in the list that follows.)
The man page wording is less clear:
KEYDEF is F[.C][OPTS][,F[.C][OPTS]] for start and stop position, where F is a field number and C a character position in the field; both are origin 1, and the stop position defaults to the line's end. If neither -t nor -b is in effect, characters in a field are counted from the beginning of the preceding whitespace. OPTS is one or more single-letter ordering options [bdfgiMhnRrV], which override global ordering options for that key. If no key is given, use the entire line as the key.
You could easily interpret that as meaning that each single-letter option overrides the global option of the same letter only. But you'd be wrong...
The POSIX definition is pretty clear (I bolded the important piece again):
The following options shall override the default ordering rules. When ordering options appear independent of any key field specifications, the requested field ordering rules shall be applied globally to all sort keys. When attached to a specific key (see -k), the specified ordering options shall override all global ordering options for that key.
The only slightly unclear part about it is that -b
doesn't appear in the list immediately following that paragraph. There's a single sentence after that list, introducing another list containing the -b
and -t
options, so you might argue that -b
isn't within the scope of the "override all global ordering options" rule.
After all, -b
isn't an ordering option, but a field splitting option, and when you attach it to a key definition you can apply it separately to the beginning, the end, or both. So it is not like any of the other options. But at least in the GNU implementation, it does follow the "override all global ordering options" rule.