Hi,

The 'test' command has the following change in POSIX 2024 [1]:

    Austin Group Defect 375 is applied, adding the
    pathname1 -ef pathname2, pathname1 -nt pathname2,
    pathname1 -ot pathname2, s1 > s2, and s1 < s2 primaries.

The -ef, -nt, and -ot operators are already implement in Coreutils.

This patch adds '>', '<' as documented [1]:

    s1 > s2
      True if s1 collates after s2 in the current locale; otherwise, false.
    s1 < s2
      True if s1 collates before s2 in the current locale; otherwise, false.

They are sort of annoying since they have to be quoted for the shell to
not interpret them as redirections. But now that they are standardized
they should probably be implemented. :)

Collin

[1] https://pubs.opengroup.org/onlinepubs/9799919799/utilities/test.html

>From 2a76b314be161eb6f2e0915e84401e7e04a0ca4b Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.fu...@gmail.com>
Date: Fri, 1 Nov 2024 20:49:16 -0700
Subject: [PATCH] test: add string operators added by POSIX 2024

* src/test.c (binop): Recognize the ">" and "<" operators.
(three_arguments): Likewise.
(binary_operator): Implement the "<" and ">" operators.
(usage): Add operators to --help output.
* tests/test/test.pl (@Tests): Add functionality tests.
* doc/coreutils.txt (test invocation, String tests): Document new
operators.
---
 doc/coreutils.texi | 12 +++++++++++-
 src/test.c         | 23 ++++++++++++++++++++---
 tests/test/test.pl |  8 ++++++++
 3 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 60a07b2ef..e016fdf26 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -13733,7 +13733,7 @@ @node test invocation
 * File type tests::             @code{-[bcdfhLpSt]}
 * Access permission tests::     @code{-[gkruwxOG]}
 * File characteristic tests::   @code{-e -s -nt -ot -ef}
-* String tests::                @code{-z -n = == !=}
+* String tests::                @code{-z -n = == != > <}
 * Numeric tests::               @code{-eq -ne -lt -le -gt -ge}
 * Connectives for test::        @code{! -a -o}
 @end menu
@@ -13942,6 +13942,16 @@ @node String tests
 @cindex not-equal string check
 True if the strings are not equal.
 
+@item @var{string1} > @var{string2}
+@opindex >
+@cindex greater-than string check
+True if @var{string1} is greater than @var{string2} in the current locale.
+
+@item @var{string1} < @var{string2}
+@opindex <
+@cindex less-than string check
+True if @var{string1} is less than @var{string2} in the current locale.
+
 @end table
 
 
diff --git a/src/test.c b/src/test.c
index 5af924347..7d3a78879 100644
--- a/src/test.c
+++ b/src/test.c
@@ -173,7 +173,7 @@ static bool
 binop (char const *s)
 {
   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "==")) ||
-          (STREQ (s,   "-nt")) ||
+          (STREQ (s,   "-nt")) || (STREQ (s, ">")) || (STREQ (s, "<")) ||
           (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
           (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
           (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
@@ -189,7 +189,7 @@ binop (char const *s)
  *	'-t' int
  *	'-'('z'|'n') string
  *	string
- *	string ('!='|'=') string
+ *	string ('!='|'='|>|<) string
  *	<int> '-'(eq|ne|le|lt|ge|gt) <int>
  *	file '-'(nt|ot|ef) file
  *	'(' <expr> ')'
@@ -371,6 +371,20 @@ binary_operator (bool l_is_l)
       return value;
     }
 
+  if (STREQ (argv[op], ">"))
+    {
+      bool value = strcoll (argv[pos], argv[pos + 2]) > 0;
+      pos += 3;
+      return value;
+    }
+
+  if (STREQ (argv[op], "<"))
+    {
+      bool value = strcoll (argv[pos], argv[pos + 2]) < 0;
+      pos += 3;
+      return value;
+    }
+
   /* Not reached.  */
   affirm (false);
 }
@@ -615,7 +629,8 @@ three_arguments (void)
       value = one_argument ();
       advance (false);
     }
-  else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o"))
+  else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o")
+           || STREQ (argv[pos + 1], ">") || STREQ (argv[pos + 1], "<"))
     value = expr ();
   else
     test_syntax_error (_("%s: binary operator expected"),
@@ -708,6 +723,8 @@ EXPRESSION is true or false and sets exit status.  It is one of:\n\
   -z STRING            the length of STRING is zero\n\
   STRING1 = STRING2    the strings are equal\n\
   STRING1 != STRING2   the strings are not equal\n\
+  STRING1 > STRING2    STRING1 is greater than STRING2 in the current locale\n\
+  STRING1 < STRING2    STRING1 is less than STRING2 in the current locale\n\
 "), stdout);
       fputs (_("\
 \n\
diff --git a/tests/test/test.pl b/tests/test/test.pl
index 0433461d8..e8509448b 100755
--- a/tests/test/test.pl
+++ b/tests/test/test.pl
@@ -185,6 +185,14 @@ my @Tests =
   ['paren-3', "'(' ')' ')'"],
   ['paren-4', "'(' ! ')'"],
   ['paren-5', "'(' -a ')'"],
+
+  ['less-collate-1', "'a' '<' 'b'"],
+  ['less-collate-2', "'a' '<' 'a'", {EXIT=>1}],
+  ['less-collate-3', "'b' '<' 'a'", {EXIT=>1}],
+
+  ['greater-collate-1', "'b' '>' 'a'"],
+  ['greater-collate-2', "'a' '>' 'a'", {EXIT=>1}],
+  ['greater-collate-3', "'a' '>' 'b'", {EXIT=>1}],
 );
 
 @Tests = add_inverse_op_tests \@Tests;
-- 
2.47.0

Reply via email to