#!/bin/sh # Tests for the Unix sort utility # Test Posix features except for locale. # Test some nonstandard features if present. # Other tests may be needed for files too big to fit in memory; # see TEST=15 below echo pathname of sort under test: which sort 2>/dev/null || whatis sort 2>/dev/null || echo can\'t tell # Initialize switches for nonstandard features. # Use parenthesized settings for supported features. o=: # officially obsolescent features: +1 -2, misplaced -o (o=) g=: # -g numeric sort including e-format numbers (g=) M=: # -M sort by month names (M=) s=: # -s stable, do not compare raw bytes on equal keys (s=) y= # -y user-specified memory size (y=-y10000) # Detect what features are supported, assuming bad options cause # errors. Set switches accordingly. echo obsolescent and nonstandard features recognized, if any: if sort +0 </dev/null 2>/dev/null; then o= echo ' +1 -2'; fi if sort /dev/null -o xx 2>/dev/null; then o= echo ' displaced -o'; fi if sort -g </dev/null 2>/dev/null; then g= echo ' -g g-format numbers'; fi if sort -M </dev/null 2>/dev/null; then M= echo ' -M months'; fi if sort -s </dev/null 2>/dev/null; then s= echo ' -s stable'; fi if sort -y10000 </dev/null 2>/dev/null; then y=-y10000 echo ' -y space'; fi if sort -z10000 </dev/null 2>/dev/null; then echo ' -z recsize (not exercised)'; fi if sort -T. </dev/null 2>/dev/null; then echo ' -T tempdir (not exercised)'; fi export TEST # major sequence number of test trap "rm -f in in1 in2 out xx -k - -o unwritablefile xsort ysort linecount; exit" 0 1 2 13 15 # "sum" must be dumb; insensitive to the # order of lines within a file. # System V sum is suitable; sum -5 is the v10 equivalent. cat >in <<! a b ! cat >in1 <<! b a ! s1="`sum in|sed '/[a-z].*/s///'`" s2="`sum in1|sed '/[a-z].*/s///'`" if test "$s1" = "$s2" then sum="sum" # sum appears to be like Sys V else s1="`sum -5 in 2>out|sed '/[a-z].*/s///'`" s2="`sum -5 in1 2>>out|sed '/[a-z].*/s///'`" if test ! -s out && test "$s1" = "$s2" then sum="sum -5" # sum appears to be like v10 else echo sum command is unsuitable for these tests exit 1 fi fi export sum # xsort testno options # Sort file "in" with specified options. # Compare with file "out" if that is supplied, # otherwise make plausibility checks on output cat <<'!' >xsort; chmod +x xsort X=$1; shift if sort "$@" in >xx && sort -c "$@" xx then if test -f out then cmp xx out >/dev/null && exit 0 echo $TEST$X comparison failed else test "`$sum <in`" = "`$sum <xx`" && exit 0 echo $TEST$X checksum failed fi else echo $TEST$X failed fi exit 1 ! # linecount testno file count # declares the given "testno" to be in error if number of # lines in "file" differs from "count" cat <<'!' >linecount; chmod +x linecount awk 'END{ if(NR!='$3') print "'$TEST$1' failed" }' $2 ! rm -f out #--------------------------------------------------------------- TEST=01; echo $TEST # -c status, checksum # obsolescent features go together cat <<! >in b a ! rm -f out -o sort -c in 2>/dev/null && echo ${TEST}A failed xsort B || '"sum"' is probably unsuitable - see comments $o sort +0 in -o in || echo ${TEST}c failed #--------------------------------------------------------------- TEST=02; echo $TEST # output from -c cat <<! >in x y ! sort -cr in >out 2>xx && echo ${TEST}A failed test -s out && echo ${TEST}B failed test -s xx && echo option -c is noisy "(probably legal)" test -s xx || echo option -c is quiet "(legal, not classical)" sort -c /dev/null 2>xx || echo ${TEST}C failed test -s xx && echo ${TEST}D failed #--------------------------------------------------------------- TEST=03; echo $TEST # -n cat <<! >in -99.0 -99.1 -.0002 -10 2 2.0x 2.0.1 0010.000000000000000000000000000000000001 10 3x x ! cat <<! >out -99.1 -99.0 -10 -.0002 x 2 2.0.1 2.0x 3x 10 0010.000000000000000000000000000000000001 ! xsort "" -n #--------------------------------------------------------------- TEST=04; echo $TEST # -b without fields, piping, -c status return cat <<! >in b a ! cp in out xsort A -b cat in | sort | cat >xx cmp xx out >/dev/null || echo ${TEST}B failed sort in | sort -cr 2>/dev/null && echo ${TEST}C failed #--------------------------------------------------------------- TEST=05; echo $TEST # fields, reverse fields, -c status return cat <<! >in b b p a b q x a ! cat <<! >out x a a b q b b p ! $o xsort A +1 -2 $o xsort B +1 -2 +2r xsort C -k 2,2 xsort D -k 2,2 -k 3r xsort E -k 2,2.0 xsort F -k 2,2 -k 1,1 -k 3 sort -c -k 2 in 2>/dev/null && ${TEST}G failed #--------------------------------------------------------------- TEST=06; echo $TEST # -t cat <<! >in a: a! ! cp in out $o xsort A -t : -r +0 $o xsort B -t : +0 -1 xsort C -t : -r -k 1 xsort D -t : -k 1,1 #--------------------------------------------------------------- TEST=07; echo $TEST # -t, character positions in fields # -t: as 1 arg is not strictly conforming, but classical cat <<! >in : ab :bac ! cat <<! >out :bac : ab ! $o xsort A -b -t: +1.1 $o xsort B -t: +1.1r xsort C -b -t: -k 2.2 xsort D -t: -k 2.2r #--------------------------------------------------------------- TEST=08; echo $TEST # space and tab as -t characters cat <<! >in b c b c b c ! cp in out xsort A -t ' ' -k2,2 xsort B -t ' ' -k2.1,2.0 cat <<! >out b c b c b c ! xsort C -t ' ' -k2,2 xsort D -t ' ' -k2.1,2.0 cat <<! >out b c b c b c ! xsort E -k2 cat <<! >out b c b c b c ! xsort F -k2b #--------------------------------------------------------------- TEST=09; echo $TEST # alphabetic as -t character cat <<! >in zXa yXa zXb ! cp in out xsort "" -tX -k2 -k1r,1 #--------------------------------------------------------------- TEST=10; echo $TEST # -m cat <<! >in a ab ab bc ca ! cat <<! >in1 Z a aa ac c ! cat <<! >out Z a a aa ab ab ac bc c ca ! sort -m in in1 >xx cmp xx out >/dev/null || echo ${TEST}A failed #--------------------------------------------------------------- TEST=11; echo $TEST # multiple files, -o overwites input, -m, -mu cat <<! >in a b c d ! sort -o xx in in in in in in in in in in in in in in in in in linecount A xx 68 sort -o in -mu in in in in in in in in in in in in in in in in in linecount B in 4 sort -o in -m in in in in in in in in in in in in in in in in in cmp in xx >/dev/null || echo ${TEST}C failed #--------------------------------------------------------------- TEST=12; echo $TEST # does -mu pick the first among equals? cat <<! >in 3B 3b 3B2 ~3B2 4.1 41 5 5. ! cat <<! >out 3B 3B2 4.1 5 ! xsort A -mudf || echo "(other behavior is legal, not classical)" xsort B -mudf -k1 || echo "(other behavior is legal, not classical)" #--------------------------------------------------------------- TEST=13; echo $TEST # long records (>8000 bytes, keys >16000), -r awk ' BEGIN { x="x" for(i=1; i<=12; i++) x = x x for(i=15; i<=25; i++) print x i }' >in awk ' BEGIN { x="x" for(i=1; i<=12; i++) x = x x for(i=25; i>=15; i--) print x i }' >out xsort A -r xsort B -k 1,1r -k 1 #--------------------------------------------------------------- TEST=14; echo $TEST "(3 long parts)" awk 'BEGIN { for(i=0; i<100000; i++) print rand() }' | grep -v e >in rm -f out xsort A; echo $TEST "(part A done)" xsort B -n; echo $TEST "(part B done)" # next test is unclean: xx is a hidden side-effect of xsort awk ' $0 < x { print "test '${TEST}C' failed"; exit } $0 "" != x { print >"out"; x = $0 } ' xx xsort C -n -u #--------------------------------------------------------------- TEST=15; echo $TEST "(long)" # force intermediate files if possible # with option -y 10000 ($y) this makes 50 intermediat # files of 80-byte records. case "$y" in "") echo "sorttest warning: inadequate test of large files; revise parameters" esac awk 'BEGIN { x = "xxxxxxxxxx" x = x x x x x x x x for(i=0; i<4000; i++) print rand(), x }' >in rm -f out xsort A -r $y rm -f in1 sort -r in -o in1 awk '$0 "x" != x { print ; x = $0 "x" }' in1 >out xsort B -u -r $y sort -r -u -m in1 -o in1 cmp in1 out >/dev/null || echo ${TEST}C failed rm in in1 out #--------------------------------------------------------------- TEST=16; echo $TEST # -nr, -nm, file name - awk 'BEGIN { for(i=-100; i<=100; i+=2) printf "%.10d\n", i }' >in awk 'BEGIN { for(i=-99; i<=100; i+=2) print i }' | sort -nr in - >xx awk '$0+0 != 101-NR { print "'${TEST}A' failed"; exit }' xx awk 'BEGIN { for(i=-99; i<=100; i+=2) print i }' | sort -mn - in >xx awk '$0+0 != -101+NR { print "'${TEST}B' failed"; exit }' xx #--------------------------------------------------------------- TEST=17; echo $TEST # -d, fields without end, modifier override cat <<! >in a-B a+b a b A+b a b ! cat <<! >out a b a b A+b a-B a+b ! $o xsort A -df +0 +0d xsort B -df -k 1 -k 1d #--------------------------------------------------------------- TEST=18; echo $TEST # -u on key only cat <<! >in 12 y 13 z 12 x ! cat <<! >out 12 x 12 y 13 z ! $o xsort A +0 -1 xsort B -k 1,1 sort -u -k 1,1 in >xx linecount C xx 2 #--------------------------------------------------------------- TEST=19; echo $TEST # -i, -d, -f cat <<! >xx.c run(i,j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } main(){ run(0, 011); /* 012=='\n' */ run(013, 0377); } ! cc xx.c a.out >in cat <<! >xx.c run(i,j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } main(){ run(0, 011); run(013, ' '-1); run(0177, 0377); run(' ', 0176); } ! cc xx.c a.out >out xsort A -i -k 2 cat <<! >xx.c run(i,j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } main(){ run(0, 010); /* 011=='\t', 012=='\n' */ run(013, ' '-1); run(' '+1, '0'-1); run('9'+1, 'A'-1); run('Z'+1, 'a'-1); run('z'+1, 0377); run('\t', '\t'); run(' ', ' '); run('0', '9'); run('A', 'Z'); run('a', 'z'); } ! cc xx.c a.out >out xsort B -d -k 2 cat <<! >xx.c run(i,j){ for( ; i<=j; i++) printf("%.3o %c\n",i,i); } main(){ int i; run(0, 011); run(013, 'A'-1); for(i='A'; i<='Z'; i++) printf("%.3o %c\n%.3o %c\n",i,i,i+040,i+040); run('Z'+1, 'a'-1); run('z'+1, 0377); } ! cc xx.c a.out >out rm xx.c xsort C -f -k 2 #--------------------------------------------------------------- TEST=20; echo $TEST # -d, -f, -b applies only to fields cat <<! >in b 'C a ! cp in out xsort A -d xsort B -f cat <<! >out b a 'C ! xsort C -dfb #--------------------------------------------------------------- TEST=21; echo $TEST # behavior of null bytes cat <<'!' >xx.c main() { printf("\n%cb\n%ca\n",0,0); } ! cc xx.c a.out >in sort -u in >xx cmp in xx >/dev/null && echo ${TEST}A failed test "`wc -c <in`" = "`wc -c <xx`" || echo ${TEST}B failed rm xx.c a.out #--------------------------------------------------------------- TEST=22; echo $TEST # field limits cat <<! >in a 2 a 1 b 2 b 1 ! cat <<! >out b 1 b 2 a 1 a 2 ! xsort "" -r -k1,1 -k2n #--------------------------------------------------------------- TEST=23; echo $TEST # empty file, compact -o echo hi >xx sort -oxx </dev/null cmp xx /dev/null >/dev/null || echo ${TEST}A failed sort -c </dev/null || echo ${TEST}B failed sort -cu </dev/null || echo ${TEST}C failed #--------------------------------------------------------------- TEST=24; echo $TEST # many fields cat <<! >in 0:2:3:4:5:6:7:8:9 1:1:3:4:5:6:7:8:9 1:2:2:4:5:6:7:8:9 1:2:3:3:5:6:7:8:9 1:2:3:4:4:6:7:8:9 1:2:3:4:5:5:7:8:9 1:2:3:4:5:6:6:8:9 1:2:3:4:5:6:7:7:9 1:2:3:4:5:6:7:8:8 ! cat <<! >out 1:2:3:4:5:6:7:8:8 1:2:3:4:5:6:7:7:9 1:2:3:4:5:6:6:8:9 1:2:3:4:5:5:7:8:9 1:2:3:4:4:6:7:8:9 1:2:3:3:5:6:7:8:9 1:2:2:4:5:6:7:8:9 1:1:3:4:5:6:7:8:9 0:2:3:4:5:6:7:8:9 ! xsort "" -t: -k9 -k8 -k7 -k6 -k5 -k4 -k3 -k2 -k1 #--------------------------------------------------------------- TEST=25; echo $TEST # variously specified alpha fields # numbers give the correct orderings cat <<! >in 01:04:19:01:16:01:21:01 a 02:03:13:15:13:19:15:02 a 03:02:07:09:07:13:09:03 a 04:01:01:03:01:07:03:04 a 05:08:20:16:17:02:20:05 aa 06:07:14:18:14:20:14:06 aa 07:06:08:10:08:14:08:07 aa 08:05:02:04:02:08:02:08 aa 09:16:22:02:22:04:24:13 b 10:15:16:20:19:22:18:14 b 11:14:10:12:10:16:12:15 b 12:13:04:06:04:10:06:16 b 13:24:24:22:24:06:22:21 bb 14:23:18:24:21:24:16:22 bb 15:22:12:14:12:18:10:23 bb 16:21:06:08:06:12:04:24 bb 17:12:21:21:18:03:19:09 ab 18:11:15:19:15:21:13:10 ab 19:10:09:11:09:15:07:11 ab 20:09:03:05:03:09:01:12 ab 21:20:23:17:23:05:23:17 ba 22:19:17:23:20:23:17:18 ba 23:18:11:13:11:17:11:19 ba 24:17:05:07:05:11:05:20 ba ! sort -k2b -k2 in >xx sort -c -t: -k2n xx 2>/dev/null || echo ${TEST}A failed sort -k2,2.1b -k2 in >xx sort -c -t: -k3n xx 2>/dev/null || echo ${TEST}B failed sort -k2.3 -k2 in >xx sort -c -t: -k4n xx 2>/dev/null || echo ${TEST}C failed sort -k2b,2.3 -k2 in >xx sort -c -t: -k5n xx 2>/dev/null || echo ${TEST}D failed sort -k2.3,2.1b -k2 in >xx sort -c -t: -k6n xx 2>/dev/null || echo ${TEST}E failed sort -k2,2.1b -k2r in >xx sort -c -t: -k7n xx 2>/dev/null || echo ${TEST}F failed sort -b -k2,2 -k2 in >xx sort -c -t: -k8n xx 2>/dev/null || echo ${TEST}G failed sort -b -k2,2b -k2 in >xx # perhaps same as G sort -c -t: -k3n xx 2>/dev/null || echo ${TEST}H failed\ "(standard is not clear on this)" #--------------------------------------------------------------- TEST=26; echo $TEST # empty fields, out of bounds fields cat <<! >in 0 5 1 4 2 3 3 2 4 1 5 0 ! cp in out xsort "" -k2.2,2.1 -k2.3,2.4 #--------------------------------------------------------------- TEST=27; echo $TEST # displaced -o rm -f out $o sort /dev/null -o out || $o echo ${TEST}B failed $o test -f out || $o echo ${TEST}C failed #--------------------------------------------------------------- TEST=28; echo $TEST # apparently nonmonotone field specs cat <<! >in aaaa c x a 0 b ! cp in out $o xsort A +1 -0.3 +1.4 -1.5 xsort B -k2,1.3 -k2.5,2.5 #--------------------------------------------------------------- TEST=29; echo $TEST # determination of end of option list cat >-k <<! x ! rm -f out -c sort -- -k </dev/null >xx || echo ${TEST}A argument failed cmp xx -k || echo ${TEST}A comparison failed cat >in <<! xxx ! >- >-o >in1 if sort -- - -o in1 <in >out 2>xx && test ! -s xx cmp in out >/dev/null || echo ${TEST}C failed test -s in1 && echo ${TEST}D failed fi #--------------------------------------------------------------- TEST=30; echo $TEST # missing newline awk 'BEGIN{ printf "%s", "x"}' | sort >xx 2>/dev/null wc -c <xx | awk '$1!=2{ print "'${TEST}' failed" }' #--------------------------------------------------------------- TEST=31; echo $TEST # -M, multiple fields cat <<! >in jan 10 1900 Feb 26 1900 feb 25 1900 January xx 1900 August 11 1900 jan 15 1990 feb 22 1990 mar 15 1990 apr 1 1990 may 45 1990 jun 14 1990 jul 4 1990 aug 1~ 1990 aug 11 1990 sep 1 1990 oct 12 1990 nov 24 1990 dec 25 1990 never 3 1990 Dec 25 1990 ! cat <<! >out January xx 1900 jan 10 1900 feb 25 1900 Feb 26 1900 August 11 1900 never 3 1990 jan 15 1990 feb 22 1990 mar 15 1990 apr 1 1990 may 45 1990 jun 14 1990 jul 4 1990 aug 1~ 1990 aug 11 1990 sep 1 1990 oct 12 1990 nov 24 1990 Dec 25 1990 dec 25 1990 ! $M xsort "" -k3n -k1M -k2n #--------------------------------------------------------------- TEST=32; echo $TEST # -M case insensitivity, -r cat <<! >in x june january december ! cat <<! >out december june january x ! $M xsort "" -Mr #--------------------------------------------------------------- TEST=33; echo $TEST # -g, big enough for IEEE floating point cat <<! >in 2 1 10 1e-1 .2 1e 1E1 1e. 3e+308 3e307 1e-308 1e-307 ! cat <<! >out 1e-308 1e-307 1e-1 .2 1 1e 1e. 2 10 1E1 3e307 3e+308 ! $g xsort "" -g #--------------------------------------------------------------- TEST=34; echo $TEST # -g wide operands cat <<! >in .99999999999999999999 099999999999999999999e-21 099999999999999999999e-19 .1e1 ! cat <<! >out 099999999999999999999e-21 .99999999999999999999 .1e1 099999999999999999999e-19 ! $g xsort A -g cat <<! >out .1e1 .99999999999999999999 099999999999999999999e-19 099999999999999999999e-21 ! xsort B -n #--------------------------------------------------------------- TEST=35; echo $TEST #-g, -u with different fp reps cat <<! >in +0 -0 0 0.10 +.1 -.1 -100e-3x x ! cat <<! >out -.1 -100e-3x +0 -0 0 x +.1 0.10 ! $g xsort A -g $g sort -gu in >xx && $g sort -c -gu xx || echo ${TEST}B failed $g linecount C xx 3 #--------------------------------------------------------------- TEST=36; echo $TEST # -s cat <<! >in a 2 b 1 c 2 a 1 b 2 c 1 ! cat <<! >out a 2 a 1 b 1 b 2 c 2 c 1 ! $s xsort "" -s -k1,1 #--------------------------------------------------------------- TEST=37; echo $TEST # -s, multiple files cat <<! >in a 2 c 2 ! cat <<! >in1 a 1 b 1 c 1 ! cat <<! >out c 2 b 1 a 2 ! $s sort -smru -k1,1 in in in1 in1 >xx $s cmp xx out >/dev/null || echo $TEST failed #--------------------------------------------------------------- TEST=38; echo $TEST # -s $s awk ' BEGIN { for(i=1; i<50; i++) for(j=1; j<=i; j++) { print i, 2 >"in" print i, 1 >"in1" } }' $s sort -m -s -k1,1n in in1 >out $s awk ' func stop() { print "'$TEST' failed"; exit } $1!=last1 { if(count!=last1 || $2!=2) stop(); count = 0} $1==last1 && $2!=last2 { if(count!=last1 || $2!=1) stop(); count = 0 } { count++; last1 = $1; last2 = $2 } ' out #--------------------------------------------------------------- TEST=39; echo $TEST # empty fields cat <<! >in bXXa aXXb ! cp in out xsort A -k3 -tX xsort B -k2 -tX xsort C -r -k2,2 -tX xsort D -r -k4 -tX #--------------------------------------------------------------- TEST=40; echo $TEST # deceptive field boundaries cat <<! >in 1.2 1.10 ! cp in out xsort A -t. -k1,1n -k2,2n xsort B -t. -k1nr -k2n cat <<! >in feb jan jan feb ! cp in out $M xsort C -k1.1,1.4M #--------------------------------------------------------------- TEST=41; echo $TEST # diagnostics echo hi >in cat <<\! >ysort error=0 warning=0 works=0 sort "$@" 2>out <in >in1 || error=1 test -s out && warning=1 test -s in1 && works=1 case $error$warning$works in 000) echo sort "$@" does not indicate trouble, but does not sort ;; 001) echo sort "$@" does not indicate trouble ;; 010) echo sort "$@" warns, does not sort, and yields exit status zero ;; 011) echo sort "$@" warns and continues ;; 100) echo sort "$@" yields nonzero exit status and does not sort ;; 101) echo sort "$@" yields nonzero exit status, but sorts ;; 111) echo sort "$@" warns and yields nonzero exit status, but sorts esac ! chmod +x ysort echo "behavior (other than message and error exit)" echo " in some questionable cases:" rm -f nosuchfile ysort nosuchfile echo x >unwritablefile chmod 0 unwritablefile ysort -o unwritablefile </dev/null cat in | ysort - - for i in -k -k0 -k-1 -k1, -kb -k1,2u -t -o do ysort $i done $g ysort -n -g $M ysort -n -M ysort -d -i ysort -n -i ysort -c in in ysort -t. -t: for i in -a -e -h -j -p -q -v -w -x -0 -1 -2 do ysort $i done $g false && ysort -g $s false && ysort -s $z false && ysort -z case "$y" in "") ysort -y esac ysort -k1,1 -f cat <<! >in A b a bc a Bd B ! cat <<! >in1 A b B a bc a Bd ! cat <<! >in2 A b B a Bd a bc ! if sort -k1,1 -f -k2,2 <in >out 2>/dev/null then echo sort -k1,1 -f -k2,2 : if cmp out in >/dev/null then echo " -f applies to fields 1 and 2" elif cmp out in1 >/dev/null then echo " -f applies to field 2 only" elif cmp out in1 >/dev/null then echo " -f ineffectual" elif cmp out /dev/null >/dev/null then echo " exit status zero, but no output" else echo " inexplicable" fi fi